From a635a485bcd926a128ffc4b78cb82105bc351360 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 23 Nov 2022 10:41:43 -0800 Subject: [PATCH] Re-added WinRT support until we're sure that it's no longer being used --- .github/workflows/msvc.yml | 3 + CMakeLists.txt | 107 ++- Makefile.in | 2 +- VisualC-GDK/SDL/SDL.vcxproj | 1 + VisualC-GDK/SDL/SDL.vcxproj.filters | 3 + VisualC-WinRT/SDL-UWP.sln | 40 + VisualC-WinRT/SDL-UWP.vcxproj | 594 ++++++++++++ VisualC-WinRT/SDL-UWP.vcxproj.filters | 849 ++++++++++++++++ VisualC/SDL/SDL.vcxproj | 1 + VisualC/SDL/SDL.vcxproj.filters | 3 + build-scripts/fnsince.pl | 6 + cmake/test/CMakeLists.txt | 4 +- docs/README-winrt.md | 519 ++++++++++ docs/README.md | 1 + include/SDL_config.h | 2 + include/SDL_config.h.cmake | 2 + include/SDL_config_windows.h | 2 + include/SDL_config_winrt.h | 220 +++++ include/SDL_cpuinfo.h | 2 +- include/SDL_hints.h | 99 ++ include/SDL_main.h | 28 + include/SDL_platform.h | 3 +- include/SDL_system.h | 103 ++ include/SDL_syswm.h | 11 + include/begin_code.h | 4 +- src/SDL.c | 4 +- src/SDL_log.c | 14 +- src/atomic/SDL_spinlock.c | 2 +- src/audio/wasapi/SDL_wasapi.c | 115 +-- src/audio/wasapi/SDL_wasapi.h | 17 + src/audio/wasapi/SDL_wasapi_win32.c | 162 ++++ src/audio/wasapi/SDL_wasapi_winrt.cpp | 447 +++++++++ src/core/windows/SDL_hid.c | 4 + src/core/windows/SDL_hid.h | 4 + src/core/windows/SDL_windows.c | 37 +- src/core/windows/SDL_windows.h | 2 + src/core/windows/SDL_xinput.c | 6 +- src/core/winrt/SDL_winrtapp_common.cpp | 67 ++ src/core/winrt/SDL_winrtapp_common.h | 31 + src/core/winrt/SDL_winrtapp_direct3d.cpp | 797 +++++++++++++++ src/core/winrt/SDL_winrtapp_direct3d.h | 92 ++ src/core/winrt/SDL_winrtapp_xaml.cpp | 160 +++ src/core/winrt/SDL_winrtapp_xaml.h | 33 + src/cpuinfo/SDL_cpuinfo.c | 4 +- src/dynapi/SDL_dynapi.h | 2 + src/dynapi/SDL_dynapi_overrides.h | 4 + src/dynapi/SDL_dynapi_procs.h | 8 + src/file/SDL_rwops.c | 3 + src/filesystem/winrt/SDL_sysfilesystem.cpp | 242 +++++ src/joystick/SDL_joystick.c | 4 + src/joystick/windows/SDL_rawinputjoystick.c | 5 + .../windows/SDL_windows_gaming_input.c | 27 +- src/joystick/windows/SDL_windowsjoystick.c | 14 +- src/joystick/windows/SDL_xinputjoystick.c | 10 +- src/loadso/windows/SDL_sysloadso.c | 8 + src/locale/winrt/SDL_syslocale.c | 58 ++ .../winrt/SDL3-WinRTResource_BlankCursor.cur | 0 src/main/winrt/SDL3-WinRTResources.rc | 3 + src/main/winrt/SDL_winrt_main_NonXAML.cpp | 54 ++ src/misc/winrt/SDL_sysurl.cpp | 41 + src/power/SDL_power.c | 3 + src/power/SDL_syspower.h | 1 + src/power/winrt/SDL_syspower.cpp | 44 + src/render/direct3d11/SDL_render_d3d11.c | 57 +- src/render/direct3d11/SDL_render_winrt.cpp | 116 +++ src/render/direct3d11/SDL_render_winrt.h | 40 + src/render/opengles2/SDL_render_gles2.c | 8 + src/thread/stdcpp/SDL_systhread.cpp | 31 + src/thread/windows/SDL_syscond_cv.c | 12 + src/thread/windows/SDL_sysmutex.c | 16 + src/thread/windows/SDL_syssem.c | 15 + src/thread/windows/SDL_systhread.c | 4 +- src/thread/windows/SDL_systls.c | 12 + src/timer/windows/SDL_systimer.c | 22 +- src/video/SDL_egl.c | 10 +- src/video/SDL_stretch.c | 2 +- src/video/SDL_sysvideo.h | 1 + src/video/SDL_video.c | 55 +- src/video/winrt/SDL_winrtevents.cpp | 153 +++ src/video/winrt/SDL_winrtevents_c.h | 83 ++ src/video/winrt/SDL_winrtgamebar.cpp | 196 ++++ src/video/winrt/SDL_winrtgamebar_cpp.h | 35 + src/video/winrt/SDL_winrtkeyboard.cpp | 466 +++++++++ src/video/winrt/SDL_winrtmessagebox.cpp | 118 +++ src/video/winrt/SDL_winrtmessagebox.h | 29 + src/video/winrt/SDL_winrtmouse.cpp | 222 +++++ src/video/winrt/SDL_winrtmouse_c.h | 40 + src/video/winrt/SDL_winrtopengles.cpp | 203 ++++ src/video/winrt/SDL_winrtopengles.h | 70 ++ src/video/winrt/SDL_winrtpointerinput.cpp | 407 ++++++++ src/video/winrt/SDL_winrtvideo.cpp | 907 ++++++++++++++++++ src/video/winrt/SDL_winrtvideo_cpp.h | 106 ++ 92 files changed, 8410 insertions(+), 164 deletions(-) create mode 100644 VisualC-WinRT/SDL-UWP.sln create mode 100644 VisualC-WinRT/SDL-UWP.vcxproj create mode 100644 VisualC-WinRT/SDL-UWP.vcxproj.filters create mode 100644 docs/README-winrt.md create mode 100644 include/SDL_config_winrt.h create mode 100644 src/audio/wasapi/SDL_wasapi_win32.c create mode 100644 src/audio/wasapi/SDL_wasapi_winrt.cpp create mode 100644 src/core/winrt/SDL_winrtapp_common.cpp create mode 100644 src/core/winrt/SDL_winrtapp_common.h create mode 100644 src/core/winrt/SDL_winrtapp_direct3d.cpp create mode 100644 src/core/winrt/SDL_winrtapp_direct3d.h create mode 100644 src/core/winrt/SDL_winrtapp_xaml.cpp create mode 100644 src/core/winrt/SDL_winrtapp_xaml.h create mode 100644 src/filesystem/winrt/SDL_sysfilesystem.cpp create mode 100644 src/locale/winrt/SDL_syslocale.c create mode 100644 src/main/winrt/SDL3-WinRTResource_BlankCursor.cur create mode 100644 src/main/winrt/SDL3-WinRTResources.rc create mode 100644 src/main/winrt/SDL_winrt_main_NonXAML.cpp create mode 100644 src/misc/winrt/SDL_sysurl.cpp create mode 100644 src/power/winrt/SDL_syspower.cpp create mode 100644 src/render/direct3d11/SDL_render_winrt.cpp create mode 100644 src/render/direct3d11/SDL_render_winrt.h create mode 100644 src/video/winrt/SDL_winrtevents.cpp create mode 100644 src/video/winrt/SDL_winrtevents_c.h create mode 100644 src/video/winrt/SDL_winrtgamebar.cpp create mode 100644 src/video/winrt/SDL_winrtgamebar_cpp.h create mode 100644 src/video/winrt/SDL_winrtkeyboard.cpp create mode 100644 src/video/winrt/SDL_winrtmessagebox.cpp create mode 100644 src/video/winrt/SDL_winrtmessagebox.h create mode 100644 src/video/winrt/SDL_winrtmouse.cpp create mode 100644 src/video/winrt/SDL_winrtmouse_c.h create mode 100644 src/video/winrt/SDL_winrtopengles.cpp create mode 100644 src/video/winrt/SDL_winrtopengles.h create mode 100644 src/video/winrt/SDL_winrtpointerinput.cpp create mode 100644 src/video/winrt/SDL_winrtvideo.cpp create mode 100644 src/video/winrt/SDL_winrtvideo_cpp.h diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml index 0af15d9eb..01d95485d 100644 --- a/.github/workflows/msvc.yml +++ b/.github/workflows/msvc.yml @@ -19,6 +19,8 @@ jobs: - { name: Windows (clang-cl x86), flags: -T ClangCL -A Win32 } - { name: Windows (ARM), flags: -A ARM } - { name: Windows (ARM64), flags: -A ARM64 } + - { name: UWP (x64), flags: -A x64 -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0" -DSDL_TESTS=OFF, nowerror: true, + project: VisualC-WinRT/SDL-UWP.sln, projectflags: '/p:Platform=x64 /p:WindowsTargetPlatformVersion=10.0.17763.0' } steps: - uses: actions/checkout@v3 @@ -57,6 +59,7 @@ jobs: echo "SDL3_DIR=$Env:GITHUB_WORKSPACE/prefix" >> $Env:GITHUB_ENV cmake --install build/ - name: Verify CMake configuration files + if: ${{ !contains(matrix.platform.name, 'UWP') }} # FIXME: cmake/test/CMakeLists.txt should support UWP run: | cmake -S cmake/test -B cmake_config_build ` -DCMAKE_PREFIX_PATH=${{ env.SDL3_DIR }} ` diff --git a/CMakeLists.txt b/CMakeLists.txt index 1386072d6..57b321016 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,12 @@ set(EXTRA_LDFLAGS) # etc. See https://github.com/libsdl-org/SDL/issues/4150 add_library(sdl-build-options INTERFACE) +if(WINDOWS_STORE) + cmake_minimum_required(VERSION 3.11.0) + target_compile_definitions(sdl-build-options INTERFACE "-DSDL_BUILDING_WINRT=1") + target_compile_options(sdl-build-options INTERFACE "-ZW") +endif() + # Build in parallel under Visual Studio. Not enabled by default. if(MSVC) target_compile_options(sdl-build-options INTERFACE "/MP") @@ -233,7 +239,7 @@ endif() # so we'll just use libusb when it's available. libusb does not support iOS, # so we default to yes on iOS. # TODO: Windows can support libusb, the hid.c file just depends on Unix APIs -if(WINDOWS OR IOS OR TVOS OR ANDROID) +if((WINDOWS AND NOT WINDOWS_STORE) OR IOS OR TVOS OR ANDROID) set(HIDAPI_SKIP_LIBUSB TRUE) else() set(HIDAPI_SKIP_LIBUSB FALSE) @@ -1664,6 +1670,11 @@ elseif(WINDOWS) file(GLOB CORE_SOURCES ${SDL3_SOURCE_DIR}/src/core/windows/*.c) list(APPEND SOURCE_FILES ${CORE_SOURCES}) + if(WINDOWS_STORE) + file(GLOB WINRT_SOURCE_FILES ${SDL3_SOURCE_DIR}/src/core/winrt/*.c ${SDL3_SOURCE_DIR}/src/core/winrt/*.cpp) + list(APPEND SOURCE_FILES ${WINRT_SOURCE_FILES}) + endif() + if(MSVC AND NOT SDL_LIBC) # Prevent codegen that would use the VC runtime libraries. set_property(DIRECTORY . APPEND PROPERTY COMPILE_OPTIONS "/GS-") @@ -1673,7 +1684,11 @@ elseif(WINDOWS) endif() if(SDL_MISC) - file(GLOB MISC_SOURCES ${SDL3_SOURCE_DIR}/src/misc/windows/*.c) + if(WINDOWS_STORE) + file(GLOB MISC_SOURCES ${SDL3_SOURCE_DIR}/src/misc/winrt/*.cpp) + else() + file(GLOB MISC_SOURCES ${SDL3_SOURCE_DIR}/src/misc/windows/*.c) + endif() list(APPEND SOURCE_FILES ${MISC_SOURCES}) set(HAVE_SDL_MISC TRUE) endif() @@ -1703,7 +1718,7 @@ elseif(WINDOWS) check_include_file(ddraw.h HAVE_DDRAW_H) check_include_file(dsound.h HAVE_DSOUND_H) check_include_file(dinput.h HAVE_DINPUT_H) - if(CMAKE_GENERATOR_PLATFORM STREQUAL "ARM") + if(WINDOWS_STORE OR CMAKE_GENERATOR_PLATFORM STREQUAL "ARM") set(HAVE_DINPUT_H 0) endif() check_include_file(dxgi.h HAVE_DXGI_H) @@ -1750,7 +1765,7 @@ elseif(WINDOWS) check_include_file(shellscalingapi.h HAVE_SHELLSCALINGAPI_H) if(SDL_AUDIO) - if(HAVE_DSOUND_H) + if(HAVE_DSOUND_H AND NOT WINDOWS_STORE) set(SDL_AUDIO_DRIVER_DSOUND 1) file(GLOB DSOUND_AUDIO_SOURCES ${SDL3_SOURCE_DIR}/src/audio/directsound/*.c) list(APPEND SOURCE_FILES ${DSOUND_AUDIO_SOURCES}) @@ -1761,6 +1776,9 @@ elseif(WINDOWS) set(SDL_AUDIO_DRIVER_WASAPI 1) set(HAVE_WASAPI TRUE) file(GLOB WASAPI_AUDIO_SOURCES ${SDL3_SOURCE_DIR}/src/audio/wasapi/*.c) + if(WINDOWS_STORE) + list(APPEND WASAPI_AUDIO_SOURCES ${SDL3_SOURCE_DIR}/src/audio/wasapi/SDL_wasapi_winrt.cpp) + endif() list(APPEND SOURCE_FILES ${WASAPI_AUDIO_SOURCES}) set(HAVE_SDL_AUDIO TRUE) endif() @@ -1771,11 +1789,20 @@ elseif(WINDOWS) if(NOT SDL_LOADSO) message_error("SDL_VIDEO requires SDL_LOADSO, which is not enabled") endif() - set(SDL_VIDEO_DRIVER_WINDOWS 1) - file(GLOB WIN_VIDEO_SOURCES ${SDL3_SOURCE_DIR}/src/video/windows/*.c) + if(WINDOWS_STORE) + set(SDL_VIDEO_DRIVER_WINRT 1) + file(GLOB WIN_VIDEO_SOURCES + ${SDL3_SOURCE_DIR}/src/video/winrt/*.c + ${SDL3_SOURCE_DIR}/src/video/winrt/*.cpp + ${SDL3_SOURCE_DIR}/src/render/direct3d11/*.cpp + ) + else() + set(SDL_VIDEO_DRIVER_WINDOWS 1) + file(GLOB WIN_VIDEO_SOURCES ${SDL3_SOURCE_DIR}/src/video/windows/*.c) + endif() list(APPEND SOURCE_FILES ${WIN_VIDEO_SOURCES}) - if(SDL_RENDER_D3D AND HAVE_D3D_H) + if(SDL_RENDER_D3D AND HAVE_D3D_H AND NOT WINDOWS_STORE) set(SDL_VIDEO_RENDER_D3D 1) set(HAVE_RENDER_D3D TRUE) endif() @@ -1783,7 +1810,7 @@ elseif(WINDOWS) set(SDL_VIDEO_RENDER_D3D11 1) set(HAVE_RENDER_D3D TRUE) endif() - if(SDL_RENDER_D3D AND HAVE_D3D12_H) + if(SDL_RENDER_D3D AND HAVE_D3D12_H AND NOT WINDOWS_STORE) set(SDL_VIDEO_RENDER_D3D12 1) set(HAVE_RENDER_D3D TRUE) endif() @@ -1803,7 +1830,7 @@ elseif(WINDOWS) set(HAVE_SDL_THREADS TRUE) endif() - if(SDL_SENSOR AND HAVE_SENSORSAPI_H) + if(SDL_SENSOR AND HAVE_SENSORSAPI_H AND NOT WINDOWS_STORE) set(SDL_SENSOR_WINDOWS 1) set(HAVE_SDL_SENSORS TRUE) file(GLOB WINDOWS_SENSOR_SOURCES ${SDL3_SOURCE_DIR}/src/sensor/windows/*.c) @@ -1811,26 +1838,50 @@ elseif(WINDOWS) endif() if(SDL_POWER) - set(SDL_POWER_WINDOWS 1) - list(APPEND SOURCE_FILES ${SDL3_SOURCE_DIR}/src/power/windows/SDL_syspower.c) - set(HAVE_SDL_POWER TRUE) + if(WINDOWS_STORE) + set(SDL_POWER_WINRT 1) + list(APPEND SOURCE_FILES ${SDL3_SOURCE_DIR}/src/power/winrt/SDL_syspower.cpp) + else() + set(SDL_POWER_WINDOWS 1) + list(APPEND SOURCE_FILES ${SDL3_SOURCE_DIR}/src/power/windows/SDL_syspower.c) + set(HAVE_SDL_POWER TRUE) + endif() endif() if(SDL_LOCALE) - file(GLOB LOCALE_SOURCES ${SDL3_SOURCE_DIR}/src/locale/windows/*.c) + if(WINDOWS_STORE) + file(GLOB LOCALE_SOURCES ${SDL3_SOURCE_DIR}/src/locale/winrt/*.c) + else() + file(GLOB LOCALE_SOURCES ${SDL3_SOURCE_DIR}/src/locale/windows/*.c) + endif() list(APPEND SOURCE_FILES ${LOCALE_SOURCES}) set(HAVE_SDL_LOCALE TRUE) endif() if(SDL_FILESYSTEM) set(SDL_FILESYSTEM_WINDOWS 1) - file(GLOB FILESYSTEM_SOURCES ${SDL3_SOURCE_DIR}/src/filesystem/windows/*.c) + if(WINDOWS_STORE) + file(GLOB FILESYSTEM_SOURCES ${SDL3_SOURCE_DIR}/src/filesystem/winrt/*.cpp) + else() + file(GLOB FILESYSTEM_SOURCES ${SDL3_SOURCE_DIR}/src/filesystem/windows/*.c) + endif() list(APPEND SOURCE_FILES ${FILESYSTEM_SOURCES}) set(HAVE_SDL_FILESYSTEM TRUE) endif() # Libraries for Win32 native and MinGW - list(APPEND EXTRA_LIBS user32 gdi32 winmm imm32 ole32 oleaut32 version uuid advapi32 setupapi shell32) + if(NOT WINDOWS_STORE) + list(APPEND EXTRA_LIBS user32 gdi32 winmm imm32 ole32 oleaut32 version uuid advapi32 setupapi shell32) + endif() + + if(WINDOWS_STORE) + list(APPEND EXTRA_LIBS + -nodefaultlib:vccorlib$<$:d> + -nodefaultlib:msvcrt$<$:d> + vccorlib$<$:d>.lib + msvcrt$<$:d>.lib + ) + endif() if(SDL_TIMERS) set(SDL_TIMER_WINDOWS 1) @@ -1850,7 +1901,7 @@ elseif(WINDOWS) list(APPEND SOURCE_FILES ${CORE_SOURCES}) if(SDL_VIDEO) - if(SDL_OPENGL) + if(SDL_OPENGL AND NOT WINDOWS_STORE) set(SDL_VIDEO_OPENGL 1) set(SDL_VIDEO_OPENGL_WGL 1) set(SDL_VIDEO_RENDER_OGL 1) @@ -1878,14 +1929,18 @@ elseif(WINDOWS) file(GLOB JOYSTICK_SOURCES ${SDL3_SOURCE_DIR}/src/joystick/windows/*.c) list(APPEND SOURCE_FILES ${JOYSTICK_SOURCES}) - set(SDL_JOYSTICK_RAWINPUT 1) + if(NOT WINDOWS_STORE) + set(SDL_JOYSTICK_RAWINPUT 1) + endif() if(HAVE_DINPUT_H) set(SDL_JOYSTICK_DINPUT 1) list(APPEND EXTRA_LIBS dinput8) endif() if(HAVE_XINPUT_H) - set(SDL_JOYSTICK_XINPUT 1) - set(HAVE_XINPUT TRUE) + if(NOT WINDOWS_STORE) + set(SDL_JOYSTICK_XINPUT 1) + set(HAVE_XINPUT TRUE) + endif() if(HAVE_WINDOWS_GAMING_INPUT_H) set(SDL_JOYSTICK_WGI 1) endif() @@ -1893,7 +1948,7 @@ elseif(WINDOWS) set(HAVE_SDL_JOYSTICK TRUE) if(SDL_HAPTIC) - if(HAVE_DINPUT_H OR HAVE_XINPUT_H) + if((HAVE_DINPUT_H OR HAVE_XINPUT_H) AND NOT WINDOWS_STORE) file(GLOB HAPTIC_SOURCES ${SDL3_SOURCE_DIR}/src/haptic/windows/*.c) if(HAVE_DINPUT_H) set(SDL_HAPTIC_DINPUT 1) @@ -3071,7 +3126,7 @@ endif() # Ensure that the extra cflags are used at compile time set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} ${EXTRA_CFLAGS_BUILD}") -if(NOT SDL3_DISABLE_SDL3MAIN) +if(NOT WINDOWS_STORE AND NOT SDL3_DISABLE_SDL3MAIN) # Build SDLmain add_library(SDL3main STATIC ${SDLMAIN_SOURCES}) add_dependencies(SDL3main sdl_headers_copy) @@ -3153,8 +3208,10 @@ if(SDL_SHARED) # Note: The clang toolset for Visual Studio does not support /NODEFAULTLIB. if(MSVC AND NOT SDL_LIBC AND NOT MSVC_CLANG AND NOT CMAKE_GENERATOR_PLATFORM STREQUAL "ARM") # Don't try to link with the default set of libraries. - set_target_properties(SDL3 PROPERTIES LINK_FLAGS_RELEASE "/NODEFAULTLIB") - set_target_properties(SDL3 PROPERTIES LINK_FLAGS_DEBUG "/NODEFAULTLIB") + if(NOT WINDOWS_STORE) + set_target_properties(SDL3 PROPERTIES LINK_FLAGS_RELEASE "/NODEFAULTLIB") + set_target_properties(SDL3 PROPERTIES LINK_FLAGS_DEBUG "/NODEFAULTLIB") + endif() set_target_properties(SDL3 PROPERTIES STATIC_LIBRARY_FLAGS "/NODEFAULTLIB") endif() # FIXME: if CMAKE_VERSION >= 3.13, use target_link_options for EXTRA_LDFLAGS @@ -3236,7 +3293,7 @@ if(NOT SDL3_DISABLE_INSTALL) RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") endif() - if(NOT SDL3_DISABLE_SDL3MAIN) + if(NOT WINDOWS_STORE AND NOT SDL3_DISABLE_SDL3MAIN) install(TARGETS SDL3main EXPORT SDL3mainTargets LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" @@ -3289,7 +3346,7 @@ if(NOT SDL3_DISABLE_INSTALL) endif() endif() - if(NOT SDL3_DISABLE_SDL3MAIN) + if(NOT WINDOWS_STORE AND NOT SDL3_DISABLE_SDL3MAIN) install(EXPORT SDL3mainTargets FILE SDL3mainTargets.cmake NAMESPACE SDL3:: diff --git a/Makefile.in b/Makefile.in index 96dfed9ac..f72a044c2 100644 --- a/Makefile.in +++ b/Makefile.in @@ -51,7 +51,7 @@ WAYLAND_SCANNER_CODE_MODE = @WAYLAND_SCANNER_CODE_MODE@ INSTALL_SDL3_CONFIG = @INSTALL_SDL3_CONFIG@ -SRC_DIST = *.md *.txt acinclude Android.mk autogen.sh android-project build-scripts cmake cmake_uninstall.cmake.in configure configure.ac docs include Makefile.* mingw sdl3-config.cmake.in sdl3-config-version.cmake.in sdl3-config.in sdl3.m4 sdl3.pc.in SDL3.spec.in SDL3Config.cmake.in src test VisualC Xcode Xcode-iOS wayland-protocols +SRC_DIST = *.md *.txt acinclude Android.mk autogen.sh android-project build-scripts cmake cmake_uninstall.cmake.in configure configure.ac docs include Makefile.* mingw sdl3-config.cmake.in sdl3-config-version.cmake.in sdl3-config.in sdl3.m4 sdl3.pc.in SDL3.spec.in SDL3Config.cmake.in src test VisualC VisualC-WinRT Xcode Xcode-iOS wayland-protocols GEN_DIST = SDL3.spec ifneq ($V,1) diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj index cc02748da..14b2d37c1 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj +++ b/VisualC-GDK/SDL/SDL.vcxproj @@ -534,6 +534,7 @@ + diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters index c8a757b1e..aee609a4c 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj.filters +++ b/VisualC-GDK/SDL/SDL.vcxproj.filters @@ -1021,6 +1021,9 @@ audio\dummy + + audio\wasapi + audio\wasapi diff --git a/VisualC-WinRT/SDL-UWP.sln b/VisualC-WinRT/SDL-UWP.sln new file mode 100644 index 000000000..62f9f0417 --- /dev/null +++ b/VisualC-WinRT/SDL-UWP.sln @@ -0,0 +1,40 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SDL3-UWP", "SDL-UWP.vcxproj", "{89E9B32E-A86A-47C3-A948-D2B1622925CE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {89E9B32E-A86A-47C3-A948-D2B1622925CE}.Debug|ARM.ActiveCfg = Debug|ARM + {89E9B32E-A86A-47C3-A948-D2B1622925CE}.Debug|ARM.Build.0 = Debug|ARM + {89E9B32E-A86A-47C3-A948-D2B1622925CE}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {89E9B32E-A86A-47C3-A948-D2B1622925CE}.Debug|ARM64.Build.0 = Debug|ARM64 + {89E9B32E-A86A-47C3-A948-D2B1622925CE}.Debug|x64.ActiveCfg = Debug|x64 + {89E9B32E-A86A-47C3-A948-D2B1622925CE}.Debug|x64.Build.0 = Debug|x64 + {89E9B32E-A86A-47C3-A948-D2B1622925CE}.Debug|x86.ActiveCfg = Debug|Win32 + {89E9B32E-A86A-47C3-A948-D2B1622925CE}.Debug|x86.Build.0 = Debug|Win32 + {89E9B32E-A86A-47C3-A948-D2B1622925CE}.Release|ARM.ActiveCfg = Release|ARM + {89E9B32E-A86A-47C3-A948-D2B1622925CE}.Release|ARM.Build.0 = Release|ARM + {89E9B32E-A86A-47C3-A948-D2B1622925CE}.Release|ARM64.ActiveCfg = Release|ARM64 + {89E9B32E-A86A-47C3-A948-D2B1622925CE}.Release|ARM64.Build.0 = Release|ARM64 + {89E9B32E-A86A-47C3-A948-D2B1622925CE}.Release|x64.ActiveCfg = Release|x64 + {89E9B32E-A86A-47C3-A948-D2B1622925CE}.Release|x64.Build.0 = Release|x64 + {89E9B32E-A86A-47C3-A948-D2B1622925CE}.Release|x86.ActiveCfg = Release|Win32 + {89E9B32E-A86A-47C3-A948-D2B1622925CE}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/VisualC-WinRT/SDL-UWP.vcxproj b/VisualC-WinRT/SDL-UWP.vcxproj new file mode 100644 index 000000000..a34357e73 --- /dev/null +++ b/VisualC-WinRT/SDL-UWP.vcxproj @@ -0,0 +1,594 @@ + + + + + Debug + ARM64 + + + Debug + ARM + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + true + + + true + + + true + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + + + {89e9b32e-a86a-47c3-a948-d2b1622925ce} + DynamicLibrary + SDL3-UWP + SDL3 + en-US + 14.0 + true + Windows Store + 8.2 + 10.0.16299.0 + 10.0.16299.0 + 10.0 + + + + DynamicLibrary + true + v142 + + + DynamicLibrary + true + v142 + + + DynamicLibrary + true + v142 + + + DynamicLibrary + true + v142 + + + DynamicLibrary + false + true + v142 + + + DynamicLibrary + false + true + v142 + + + DynamicLibrary + false + true + v142 + + + DynamicLibrary + false + true + v142 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + SDL3 + + + false + false + SDL3 + + + false + false + SDL3 + + + false + false + SDL3 + + + false + false + SDL3 + + + false + false + SDL3 + + + false + false + SDL3 + + + false + false + SDL3 + + + + NotUsing + false + ..\include;%(AdditionalIncludeDirectories) + DLL_EXPORT;_CRT_SECURE_NO_WARNINGS;SDL_BUILDING_WINRT=1;%(PreprocessorDefinitions) + + + Console + false + false + /nodefaultlib:vccorlibd /nodefaultlib:msvcrtd vccorlibd.lib msvcrtd.lib %(AdditionalOptions) + + + + + NotUsing + false + ..\include;%(AdditionalIncludeDirectories) + DLL_EXPORT;_CRT_SECURE_NO_WARNINGS;SDL_BUILDING_WINRT=1;%(PreprocessorDefinitions) + + + Console + false + false + /nodefaultlib:vccorlib /nodefaultlib:msvcrt vccorlib.lib msvcrt.lib %(AdditionalOptions) + + + + + NotUsing + false + ..\include;%(AdditionalIncludeDirectories) + DLL_EXPORT;_CRT_SECURE_NO_WARNINGS;SDL_BUILDING_WINRT=1;%(PreprocessorDefinitions) + + + Console + false + false + /nodefaultlib:vccorlibd /nodefaultlib:msvcrtd vccorlibd.lib msvcrtd.lib %(AdditionalOptions) + + + + + NotUsing + false + ..\include;%(AdditionalIncludeDirectories) + DLL_EXPORT;_CRT_SECURE_NO_WARNINGS;SDL_BUILDING_WINRT=1;%(PreprocessorDefinitions) + + + Console + false + false + /nodefaultlib:vccorlib /nodefaultlib:msvcrt vccorlib.lib msvcrt.lib %(AdditionalOptions) + + + + + NotUsing + false + ..\include;%(AdditionalIncludeDirectories) + DLL_EXPORT;_CRT_SECURE_NO_WARNINGS;SDL_BUILDING_WINRT=1;%(PreprocessorDefinitions) + + + Console + false + false + /nodefaultlib:vccorlibd /nodefaultlib:msvcrtd vccorlibd.lib msvcrtd.lib %(AdditionalOptions) + + + + + NotUsing + false + ..\include;%(AdditionalIncludeDirectories) + DLL_EXPORT;_CRT_SECURE_NO_WARNINGS;SDL_BUILDING_WINRT=1;%(PreprocessorDefinitions) + + + Console + false + false + /nodefaultlib:vccorlib /nodefaultlib:msvcrt vccorlib.lib msvcrt.lib %(AdditionalOptions) + + + + + NotUsing + false + ..\include;%(AdditionalIncludeDirectories) + DLL_EXPORT;_CRT_SECURE_NO_WARNINGS;SDL_BUILDING_WINRT=1;%(PreprocessorDefinitions) + + + Console + false + false + /nodefaultlib:vccorlibd /nodefaultlib:msvcrtd vccorlibd.lib msvcrtd.lib %(AdditionalOptions) + + + + + NotUsing + false + ..\include;%(AdditionalIncludeDirectories) + DLL_EXPORT;_CRT_SECURE_NO_WARNINGS;SDL_BUILDING_WINRT=1;%(PreprocessorDefinitions) + + + Console + false + false + /nodefaultlib:vccorlib /nodefaultlib:msvcrt vccorlib.lib msvcrt.lib %(AdditionalOptions) + + + + + + diff --git a/VisualC-WinRT/SDL-UWP.vcxproj.filters b/VisualC-WinRT/SDL-UWP.vcxproj.filters new file mode 100644 index 000000000..6599cdabb --- /dev/null +++ b/VisualC-WinRT/SDL-UWP.vcxproj.filters @@ -0,0 +1,849 @@ + + + + + {fa0ff2df-c3d6-498a-96f1-1f88e7ce0da3} + + + {68e1b30b-19ed-4612-93e4-6260c5a979e5} + + + + + 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 + + + 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 + + + Header 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 + + + 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 + + + \ No newline at end of file diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj index d1adc6b98..40eed8f14 100644 --- a/VisualC/SDL/SDL.vcxproj +++ b/VisualC/SDL/SDL.vcxproj @@ -452,6 +452,7 @@ + diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters index d5cb6a87b..b87cc866b 100644 --- a/VisualC/SDL/SDL.vcxproj.filters +++ b/VisualC/SDL/SDL.vcxproj.filters @@ -1012,6 +1012,9 @@ audio\dummy + + audio\wasapi + audio\wasapi diff --git a/build-scripts/fnsince.pl b/build-scripts/fnsince.pl index 217635f24..53a30888e 100755 --- a/build-scripts/fnsince.pl +++ b/build-scripts/fnsince.pl @@ -88,6 +88,12 @@ foreach my $release (@releases) { close(PIPEFH); } +# these are incorrect in the dynapi header, because we forgot to add them +# until a later release, but are available in the older release. +$funcs{'SDL_WinRTGetFSPathUNICODE'} = '2.0.3'; +$funcs{'SDL_WinRTGetFSPathUTF8'} = '2.0.3'; +$funcs{'SDL_WinRTRunApp'} = '2.0.3'; + if (not defined $wikipath) { foreach my $release (@releases) { foreach my $fn (sort keys %funcs) { diff --git a/cmake/test/CMakeLists.txt b/cmake/test/CMakeLists.txt index 747b387d0..d4e488c54 100644 --- a/cmake/test/CMakeLists.txt +++ b/cmake/test/CMakeLists.txt @@ -29,7 +29,7 @@ add_feature_info("TEST_STATIC" TEST_STATIC "Test linking with static library") if(TEST_SHARED) find_package(SDL3 REQUIRED CONFIG COMPONENTS SDL3) - if(EMSCRIPTEN OR WIN32) + if(EMSCRIPTEN OR (WIN32 AND NOT WINDOWS_STORE)) find_package(SDL3 REQUIRED CONFIG COMPONENTS SDL3main) endif() add_executable(gui-shared WIN32 main_gui.c) @@ -79,7 +79,7 @@ endif() if(TEST_STATIC) find_package(SDL3 REQUIRED CONFIG COMPONENTS SDL3-static) - if(EMSCRIPTEN OR WIN32) + if(EMSCRIPTEN OR (WIN32 AND NOT WINDOWS_STORE)) find_package(SDL3 REQUIRED CONFIG COMPONENTS SDL3main) endif() add_executable(gui-static WIN32 main_gui.c) diff --git a/docs/README-winrt.md b/docs/README-winrt.md new file mode 100644 index 000000000..68271cc65 --- /dev/null +++ b/docs/README-winrt.md @@ -0,0 +1,519 @@ +WinRT +===== + +This port allows SDL applications to run on Microsoft's platforms that require +use of "Windows Runtime", aka. "WinRT", APIs. Microsoft may, in some cases, +refer to them as either "Windows Store", or for Windows 10, "UWP" apps. + +In the past, SDL has supported Windows RT 8.x, Windows Phone, etc, but in +modern times this port is focused on UWP apps, which run on Windows 10, +and modern Xbox consoles. + + +Requirements +------------ + +* Microsoft Visual C++ (aka Visual Studio) 2019. + - Free, "Community" or "Express" editions may be used, so long as they + include support for either "Windows Store" or "Windows Phone" apps. + "Express" versions marked as supporting "Windows Desktop" development + typically do not include support for creating WinRT apps, to note. + (The "Community" editions of Visual C++ do, however, support both + desktop/Win32 and WinRT development). +* A valid Microsoft account - This requirement is not imposed by SDL, but + rather by Microsoft's Visual C++ toolchain. This is required to launch or + debug apps. + + +Status +------ + +Here is a rough list of what works, and what doesn't: + +* What works: + * compilation via Visual C++ 2019. + * compile-time platform detection for SDL programs. The C/C++ #define, + `__WINRT__`, will be set to 1 (by SDL) when compiling for WinRT. + * GPU-accelerated 2D rendering, via SDL_Renderer. + * OpenGL ES 2, via the ANGLE library (included separately from SDL) + * software rendering, via either SDL_Surface (optionally in conjunction with + SDL_GetWindowSurface() and SDL_UpdateWindowSurface()) or via the + SDL_Renderer APIs + * threads + * timers (via SDL_GetTicks(), SDL_AddTimer(), SDL_GetPerformanceCounter(), + SDL_GetPerformanceFrequency(), etc.) + * file I/O via SDL_RWops + * mouse input (unsupported on Windows Phone) + * audio, via SDL's WASAPI backend (if you want to record, your app must + have "Microphone" capabilities enabled in its manifest, and the user must + not have blocked access. Otherwise, capture devices will fail to work, + presenting as a device disconnect shortly after opening it.) + * .DLL file loading. Libraries *MUST* be packaged inside applications. Loading + anything outside of the app is not supported. + * system path retrieval via SDL's filesystem APIs + * game controllers. Support is provided via the SDL_Joystick and + SDL_GameController APIs, and is backed by Microsoft's XInput API. Please + note, however, that Windows limits game-controller support in UWP apps to, + "Xbox compatible controllers" (many controllers that work in Win32 apps, + do not work in UWP, due to restrictions in UWP itself.) + * multi-touch input + * app events. SDL_APP_WILLENTER* and SDL_APP_DIDENTER* events get sent out as + appropriate. + * window events + * using Direct3D 11.x APIs outside of SDL. Non-XAML / Direct3D-only apps can + choose to render content directly via Direct3D, using SDL to manage the + internal WinRT window, as well as input and audio. (Use + SDL_GetWindowWMInfo() to get the WinRT 'CoreWindow', and pass it into + IDXGIFactory2::CreateSwapChainForCoreWindow() as appropriate.) + +* What partially works: + * keyboard input. Most of WinRT's documented virtual keys are supported, as + well as many keys with documented hardware scancodes. Converting + SDL_Scancodes to or from SDL_Keycodes may not work, due to missing APIs + (MapVirtualKey()) in Microsoft's Windows Store / UWP APIs. + * SDLmain. WinRT uses a different signature for each app's main() function. + SDL-based apps that use this port must compile in SDL_winrt_main_NonXAML.cpp + (in `SDL\src\main\winrt\`) directly in order for their C-style main() + functions to be called. + +* What doesn't work: + * compilation with anything other than Visual C++ + * programmatically-created custom cursors. These don't appear to be supported + by WinRT. Different OS-provided cursors can, however, be created via + SDL_CreateSystemCursor() (unsupported on Windows Phone) + * SDL_WarpMouseInWindow() or SDL_WarpMouseGlobal(). This are not currently + supported by WinRT itself. + * joysticks and game controllers that either are not supported by + Microsoft's XInput API, or are not supported within UWP apps (many + controllers that work in Win32, do not work in UWP, due to restrictions in + UWP itself). + * turning off VSync when rendering on Windows Phone. Attempts to turn VSync + off on Windows Phone result either in Direct3D not drawing anything, or it + forcing VSync back on. As such, SDL_RENDERER_PRESENTVSYNC will always get + turned-on on Windows Phone. This limitation is not present in non-Phone + WinRT (such as Windows 8.x), where turning off VSync appears to work. + * probably anything else that's not listed as supported + + + +Upgrade Notes +------------- + +#### SDL_GetPrefPath() usage when upgrading WinRT apps from SDL 2.0.3 + +SDL 2.0.4 fixes two bugs found in the WinRT version of SDL_GetPrefPath(). +The fixes may affect older, SDL 2.0.3-based apps' save data. Please note +that these changes only apply to SDL-based WinRT apps, and not to apps for +any other platform. + +1. SDL_GetPrefPath() would return an invalid path, one in which the path's + directory had not been created. Attempts to create files there + (via fopen(), for example), would fail, unless that directory was + explicitly created beforehand. + +2. SDL_GetPrefPath(), for non-WinPhone-based apps, would return a path inside + a WinRT 'Roaming' folder, the contents of which get automatically + synchronized across multiple devices. This process can occur while an + application runs, and can cause existing save-data to be overwritten + at unexpected times, with data from other devices. (Windows Phone apps + written with SDL 2.0.3 did not utilize a Roaming folder, due to API + restrictions in Windows Phone 8.0). + + +SDL_GetPrefPath(), starting with SDL 2.0.4, addresses these by: + +1. making sure that SDL_GetPrefPath() returns a directory in which data + can be written to immediately, without first needing to create directories. + +2. basing SDL_GetPrefPath() off of a different, non-Roaming folder, the + contents of which do not automatically get synchronized across devices + (and which require less work to use safely, in terms of data integrity). + +Apps that wish to get their Roaming folder's path can do so either by using +SDL_WinRTGetFSPathUTF8(), SDL_WinRTGetFSPathUNICODE() (which returns a +UCS-2/wide-char string), or directly through the WinRT class, +Windows.Storage.ApplicationData. + + + +Setup, High-Level Steps +----------------------- + +The steps for setting up a project for an SDL/WinRT app looks like the +following, at a high-level: + +1. create a new Visual C++ project using Microsoft's template for a, + "Direct3D App". +2. remove most of the files from the project. +3. make your app's project directly reference SDL/WinRT's own Visual C++ + project file, via use of Visual C++'s "References" dialog. This will setup + the linker, and will copy SDL's .dll files to your app's final output. +4. adjust your app's build settings, at minimum, telling it where to find SDL's + header files. +5. add files that contains a WinRT-appropriate main function, along with some + data to make sure mouse-cursor-hiding (via SDL_ShowCursor(SDL_DISABLE) calls) + work properly. +6. add SDL-specific app code. +7. build and run your app. + + +Setup, Detailed Steps +--------------------- + +### 1. Create a new project ### + +Create a new project using one of Visual C++'s templates for a plain, non-XAML, +"Direct3D App" (XAML support for SDL/WinRT is not yet ready for use). If you +don't see one of these templates, in Visual C++'s 'New Project' dialog, try +using the textbox titled, 'Search Installed Templates' to look for one. + + +### 2. Remove unneeded files from the project ### + +In the new project, delete any file that has one of the following extensions: + +- .cpp +- .h +- .hlsl + +When you are done, you should be left with a few files, each of which will be a +necessary part of your app's project. These files will consist of: + +- an .appxmanifest file, which contains metadata on your WinRT app. This is + similar to an Info.plist file on iOS, or an AndroidManifest.xml on Android. +- a few .png files, one of which is a splash screen (displayed when your app + launches), others are app icons. +- a .pfx file, used for code signing purposes. + + +### 3. Add references to SDL's project files ### + +SDL/WinRT can be built in multiple variations, spanning across three different +CPU architectures (x86, x64, and ARM) and two different configurations +(Debug and Release). WinRT and Visual C++ do not currently provide a means +for combining multiple variations of one library into a single file. +Furthermore, it does not provide an easy means for copying pre-built .dll files +into your app's final output (via Post-Build steps, for example). It does, +however, provide a system whereby an app can reference the MSVC projects of +libraries such that, when the app is built: + +1. each library gets built for the appropriate CPU architecture(s) and WinRT + platform(s). +2. each library's output, such as .dll files, get copied to the app's build + output. + +To set this up for SDL/WinRT, you'll need to run through the following steps: + +1. open up the Solution Explorer inside Visual C++ (under the "View" menu, then + "Solution Explorer") +2. right click on your app's solution. +3. navigate to "Add", then to "Existing Project..." +4. find SDL/WinRT's Visual C++ project file and open it, in the `VisualC-WinRT` + directory. +5. once the project has been added, right-click on your app's project and + select, "References..." +6. click on the button titled, "Add New Reference..." +7. check the box next to SDL +8. click OK to close the dialog +9. SDL will now show up in the list of references. Click OK to close that + dialog. + +Your project is now linked to SDL's project, insofar that when the app is +built, SDL will be built as well, with its build output getting included with +your app. + + +### 4. Adjust Your App's Build Settings ### + +Some build settings need to be changed in your app's project. This guide will +outline the following: + +- making sure that the compiler knows where to find SDL's header files +- **Optional for C++, but NECESSARY for compiling C code:** telling the + compiler not to use Microsoft's C++ extensions for WinRT development. +- **Optional:** telling the compiler not generate errors due to missing + precompiled header files. + +To change these settings: + +1. right-click on the project +2. choose "Properties" +3. in the drop-down box next to "Configuration", choose, "All Configurations" +4. in the drop-down box next to "Platform", choose, "All Platforms" +5. in the left-hand list, expand the "C/C++" section +6. select "General" +7. edit the "Additional Include Directories" setting, and add a path to SDL's + "include" directory +8. **Optional: to enable compilation of C code:** change the setting for + "Consume Windows Runtime Extension" from "Yes (/ZW)" to "No". If you're + working with a completely C++ based project, this step can usually be + omitted. +9. **Optional: to disable precompiled headers (which can produce + 'stdafx.h'-related build errors, if setup incorrectly:** in the left-hand + list, select "Precompiled Headers", then change the setting for "Precompiled + Header" from "Use (/Yu)" to "Not Using Precompiled Headers". +10. close the dialog, saving settings, by clicking the "OK" button + + +### 5. Add a WinRT-appropriate main function, and a blank-cursor image, to the app. ### + +A few files should be included directly in your app's MSVC project, specifically: +1. a WinRT-appropriate main function (which is different than main() functions on + other platforms) +2. a Win32-style cursor resource, used by SDL_ShowCursor() to hide the mouse cursor + (if and when the app needs to do so). *If this cursor resource is not + included, mouse-position reporting may fail if and when the cursor is + hidden, due to possible bugs/design-oddities in Windows itself.* + +To include these files for C/C++ projects: + +1. right-click on your project (again, in Visual C++'s Solution Explorer), + navigate to "Add", then choose "Existing Item...". +2. navigate to the directory containing SDL's source code, then into its + subdirectory, 'src/main/winrt/'. Select, then add, the following files: + - `SDL_winrt_main_NonXAML.cpp` + - `SDL3-WinRTResources.rc` + - `SDL3-WinRTResource_BlankCursor.cur` +3. right-click on the file `SDL_winrt_main_NonXAML.cpp` (as listed in your + project), then click on "Properties...". +4. in the drop-down box next to "Configuration", choose, "All Configurations" +5. in the drop-down box next to "Platform", choose, "All Platforms" +6. in the left-hand list, click on "C/C++" +7. change the setting for "Consume Windows Runtime Extension" to "Yes (/ZW)". +8. click the OK button. This will close the dialog. + +**NOTE: C++/CX compilation is currently required in at least one file of your +app's project. This is to make sure that Visual C++'s linker builds a 'Windows +Metadata' file (.winmd) for your app. Not doing so can lead to build errors.** + +For non-C++ projects, you will need to call SDL_WinRTRunApp from your language's +main function, and generate SDL3-WinRTResources.res manually by using `rc` via +the Developer Command Prompt and including it as a within the +first block in your Visual Studio project file. + +### 6. Add app code and assets ### + +At this point, you can add in SDL-specific source code. Be sure to include a +C-style main function (ie: `int main(int argc, char *argv[])`). From there you +should be able to create a single `SDL_Window` (WinRT apps can only have one +window, at present), as well as an `SDL_Renderer`. Direct3D will be used to +draw content. Events are received via SDL's usual event functions +(`SDL_PollEvent`, etc.) If you have a set of existing source files and assets, +you can start adding them to the project now. If not, or if you would like to +make sure that you're setup correctly, some short and simple sample code is +provided below. + + +#### 6.A. ... when creating a new app #### + +If you are creating a new app (rather than porting an existing SDL-based app), +or if you would just like a simple app to test SDL/WinRT with before trying to +get existing code working, some working SDL/WinRT code is provided below. To +set this up: + +1. right click on your app's project +2. select Add, then New Item. An "Add New Item" dialog will show up. +3. from the left-hand list, choose "Visual C++" +4. from the middle/main list, choose "C++ File (.cpp)" +5. near the bottom of the dialog, next to "Name:", type in a name for your +source file, such as, "main.cpp". +6. click on the Add button. This will close the dialog, add the new file to +your project, and open the file in Visual C++'s text editor. +7. Copy and paste the following code into the new file, then save it. + +```c +#include + +int main(int argc, char **argv) +{ + SDL_DisplayMode mode; + SDL_Window * window = NULL; + SDL_Renderer * renderer = NULL; + SDL_Event evt; + SDL_bool keep_going = SDL_TRUE; + + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + return 1; + } else if (SDL_GetCurrentDisplayMode(0, &mode) != 0) { + return 1; + } else if (SDL_CreateWindowAndRenderer(mode.w, mode.h, SDL_WINDOW_FULLSCREEN, &window, &renderer) != 0) { + return 1; + } + + while (keep_going) { + while (SDL_PollEvent(&evt)) { + if ((evt.type == SDL_KEYDOWN) && (evt.key.keysym.sym == SDLK_ESCAPE)) { + keep_going = SDL_FALSE; + } + } + + SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); + SDL_RenderClear(renderer); + SDL_RenderPresent(renderer); + } + + SDL_Quit(); + return 0; +} +``` + +#### 6.B. Adding code and assets #### + +If you have existing code and assets that you'd like to add, you should be able +to add them now. The process for adding a set of files is as such. + +1. right click on the app's project +2. select Add, then click on "New Item..." +3. open any source, header, or asset files as appropriate. Support for C and +C++ is available. + +Do note that WinRT only supports a subset of the APIs that are available to +Win32-based apps. Many portions of the Win32 API and the C runtime are not +available. + +A list of unsupported C APIs can be found at + + +General information on using the C runtime in WinRT can be found at + + +A list of supported Win32 APIs for WinRT apps can be found at +. To note, +the list of supported Win32 APIs for Windows Phone 8.0 is different. +That list can be found at + + + +### 7. Build and run your app ### + +Your app project should now be setup, and you should be ready to build your app. +To run it on the local machine, open the Debug menu and choose "Start +Debugging". This will build your app, then run your app full-screen. To switch +out of your app, press the Windows key. Alternatively, you can choose to run +your app in a window. To do this, before building and running your app, find +the drop-down menu in Visual C++'s toolbar that says, "Local Machine". Expand +this by clicking on the arrow on the right side of the list, then click on +Simulator. Once you do that, any time you build and run the app, the app will +launch in window, rather than full-screen. + + +#### 7.A. Running apps on older, ARM-based, "Windows RT" devices #### + +**These instructions do not include Windows Phone, despite Windows Phone +typically running on ARM processors.** They are specifically for devices +that use the "Windows RT" operating system, which was a modified version of +Windows 8.x that ran primarily on ARM-based tablet computers. + +To build and run the app on ARM-based, "Windows RT" devices, you'll need to: + +- install Microsoft's "Remote Debugger" on the device. Visual C++ installs and + debugs ARM-based apps via IP networks. +- change a few options on the development machine, both to make sure it builds + for ARM (rather than x86 or x64), and to make sure it knows how to find the + Windows RT device (on the network). + +Microsoft's Remote Debugger can be found at +. Please note +that separate versions of this debugger exist for different versions of Visual +C++, one each for MSVC 2015, 2013, and 2012. + +To setup Visual C++ to launch your app on an ARM device: + +1. make sure the Remote Debugger is running on your ARM device, and that it's on + the same IP network as your development machine. +2. from Visual C++'s toolbar, find a drop-down menu that says, "Win32". Click + it, then change the value to "ARM". +3. make sure Visual C++ knows the hostname or IP address of the ARM device. To + do this: + 1. open the app project's properties + 2. select "Debugging" + 3. next to "Machine Name", enter the hostname or IP address of the ARM + device + 4. if, and only if, you've turned off authentication in the Remote Debugger, + then change the setting for "Require Authentication" to No + 5. click "OK" +4. build and run the app (from Visual C++). The first time you do this, a + prompt will show up on the ARM device, asking for a Microsoft Account. You + do, unfortunately, need to log in here, and will need to follow the + subsequent registration steps in order to launch the app. After you do so, + if the app didn't already launch, try relaunching it again from within Visual + C++. + + +Troubleshooting +--------------- + +#### Build fails with message, "error LNK2038: mismatch detected for 'vccorlib_lib_should_be_specified_before_msvcrt_lib_to_linker'" + +Try adding the following to your linker flags. In MSVC, this can be done by +right-clicking on the app project, navigating to Configuration Properties -> +Linker -> Command Line, then adding them to the Additional Options +section. + +* For Release builds / MSVC-Configurations, add: + + /nodefaultlib:vccorlib /nodefaultlib:msvcrt vccorlib.lib msvcrt.lib + +* For Debug builds / MSVC-Configurations, add: + + /nodefaultlib:vccorlibd /nodefaultlib:msvcrtd vccorlibd.lib msvcrtd.lib + + +#### Mouse-motion events fail to get sent, or SDL_GetMouseState() fails to return updated values + +This may be caused by a bug in Windows itself, whereby hiding the mouse +cursor can cause mouse-position reporting to fail. + +SDL provides a workaround for this, but it requires that an app links to a +set of Win32-style cursor image-resource files. A copy of suitable resource +files can be found in `src/main/winrt/`. Adding them to an app's Visual C++ +project file should be sufficient to get the app to use them. + + +#### SDL's Visual Studio project file fails to open, with message, "The system can't find the file specified." + +This can be caused for any one of a few reasons, which Visual Studio can +report, but won't always do so in an up-front manner. + +To help determine why this error comes up: + +1. open a copy of Visual Studio without opening a project file. This can be + accomplished via Windows' Start Menu, among other means. +2. show Visual Studio's Output window. This can be done by going to VS' + menu bar, then to View, and then to Output. +3. try opening the SDL project file directly by going to VS' menu bar, then + to File, then to Open, then to Project/Solution. When a File-Open dialog + appears, open the SDL project (such as the one in SDL's source code, in its + directory, VisualC-WinRT/UWP_VS2015/). +4. after attempting to open SDL's Visual Studio project file, additional error + information will be output to the Output window. + +If Visual Studio reports (via its Output window) that the project: + +"could not be loaded because it's missing install components. To fix this launch Visual Studio setup with the following selections: +Microsoft.VisualStudio.ComponentGroup.UWP.VC" + +... then you will need to re-launch Visual Studio's installer, and make sure that +the workflow for "Universal Windows Platform development" is checked, and that its +optional component, "C++ Universal Windows Platform tools" is also checked. While +you are there, if you are planning on targeting UWP / Windows 10, also make sure +that you check the optional component, "Windows 10 SDK (10.0.10240.0)". After +making sure these items are checked as-appropriate, install them. + +Once you install these components, try re-launching Visual Studio, and re-opening +the SDL project file. If you still get the error dialog, try using the Output +window, again, seeing what Visual Studio says about it. + + +#### Game controllers / joysticks aren't working! + +Windows only permits certain game controllers and joysticks to work within +WinRT / UWP apps. Even if a game controller or joystick works in a Win32 +app, that device is not guaranteed to work inside a WinRT / UWP app. + +According to Microsoft, "Xbox compatible controllers" should work inside +UWP apps, potentially with more working in the future. This includes, but +may not be limited to, Microsoft-made Xbox controllers and USB adapters. +(Source: https://social.msdn.microsoft.com/Forums/en-US/9064838b-e8c3-4c18-8a83-19bf0dfe150d/xinput-fails-to-detect-game-controllers?forum=wpdevelop) + + diff --git a/docs/README.md b/docs/README.md index 064b87469..584422365 100644 --- a/docs/README.md +++ b/docs/README.md @@ -41,6 +41,7 @@ More documentation and FAQs are available online at [the wiki](http://wiki.libsd - [Touch](README-touch.md) - [Versions](README-versions.md) - [Windows](README-windows.md) +- [WinRT](README-winrt.md) - [PSVita](README-vita.md) - [Nokia N-Gage](README-ngage.md) diff --git a/include/SDL_config.h b/include/SDL_config.h index 5387db382..f59b6bbff 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_winrt.h" #elif defined(__WINGDK__) #include "SDL_config_wingdk.h" #elif defined(__XBOXONE__) || defined(__XBOXSERIES__) diff --git a/include/SDL_config.h.cmake b/include/SDL_config.h.cmake index c52ca5437..cebc9007b 100644 --- a/include/SDL_config.h.cmake +++ b/include/SDL_config.h.cmake @@ -390,6 +390,7 @@ #cmakedefine SDL_VIDEO_DRIVER_DUMMY @SDL_VIDEO_DRIVER_DUMMY@ #cmakedefine SDL_VIDEO_DRIVER_OFFSCREEN @SDL_VIDEO_DRIVER_OFFSCREEN@ #cmakedefine SDL_VIDEO_DRIVER_WINDOWS @SDL_VIDEO_DRIVER_WINDOWS@ +#cmakedefine SDL_VIDEO_DRIVER_WINRT @SDL_VIDEO_DRIVER_WINRT@ #cmakedefine SDL_VIDEO_DRIVER_WAYLAND @SDL_VIDEO_DRIVER_WAYLAND@ #cmakedefine SDL_VIDEO_DRIVER_RPI @SDL_VIDEO_DRIVER_RPI@ #cmakedefine SDL_VIDEO_DRIVER_VIVANTE @SDL_VIDEO_DRIVER_VIVANTE@ @@ -463,6 +464,7 @@ #cmakedefine SDL_POWER_ANDROID @SDL_POWER_ANDROID@ #cmakedefine SDL_POWER_LINUX @SDL_POWER_LINUX@ #cmakedefine SDL_POWER_WINDOWS @SDL_POWER_WINDOWS@ +#cmakedefine SDL_POWER_WINRT @SDL_POWER_WINRT@ #cmakedefine SDL_POWER_MACOSX @SDL_POWER_MACOSX@ #cmakedefine SDL_POWER_UIKIT @SDL_POWER_UIKIT@ #cmakedefine SDL_POWER_HAIKU @SDL_POWER_HAIKU@ diff --git a/include/SDL_config_windows.h b/include/SDL_config_windows.h index 97e7ac82b..2ea2f9d25 100644 --- a/include/SDL_config_windows.h +++ b/include/SDL_config_windows.h @@ -240,7 +240,9 @@ typedef unsigned int uintptr_t; /* Enable various input drivers */ #define SDL_JOYSTICK_DINPUT 1 #define SDL_JOYSTICK_HIDAPI 1 +#ifndef __WINRT__ #define SDL_JOYSTICK_RAWINPUT 1 +#endif #define SDL_JOYSTICK_VIRTUAL 1 #ifdef HAVE_WINDOWS_GAMING_INPUT_H #define SDL_JOYSTICK_WGI 1 diff --git a/include/SDL_config_winrt.h b/include/SDL_config_winrt.h new file mode 100644 index 000000000..da894c883 --- /dev/null +++ b/include/SDL_config_winrt.h @@ -0,0 +1,220 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_winrt_h_ +#define SDL_config_winrt_h_ +#define SDL_config_h_ + +#include "SDL_platform.h" + +/* Make sure the Windows SDK's NTDDI_VERSION macro gets defined. This is used + by SDL to determine which version of the Windows SDK is being used. +*/ +#include + +/* Define possibly-undefined NTDDI values (used when compiling SDL against + older versions of the Windows SDK. +*/ +#ifndef NTDDI_WINBLUE +#define NTDDI_WINBLUE 0x06030000 +#endif +#ifndef NTDDI_WIN10 +#define NTDDI_WIN10 0x0A000000 +#endif + +/* This is a set of defines to configure the SDL features */ + +#ifdef _WIN64 +# define SIZEOF_VOIDP 8 +#else +# define SIZEOF_VOIDP 4 +#endif + +#ifdef __clang__ +# define HAVE_GCC_ATOMICS 1 +#endif + +/* Useful headers */ +#define HAVE_DXGI_H 1 +#if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP +#define HAVE_XINPUT_H 1 +#endif + +#define HAVE_MMDEVICEAPI_H 1 +#define HAVE_AUDIOCLIENT_H 1 +#define HAVE_TPCSHRD_H 1 + +#define HAVE_LIBC 1 +#define STDC_HEADERS 1 +#define HAVE_CTYPE_H 1 +#define HAVE_FLOAT_H 1 +#define HAVE_LIMITS_H 1 +#define HAVE_MATH_H 1 +#define HAVE_SIGNAL_H 1 +#define HAVE_STDINT_H 1 +#define HAVE_STDIO_H 1 +#define HAVE_STRING_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_BSEARCH 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_STRCHR 1 +#define HAVE_STRRCHR 1 +#define HAVE_STRSTR 1 +#define HAVE_STRTOL 1 +#define HAVE_STRTOUL 1 +/* #undef HAVE_STRTOLL */ +/* #undef HAVE_STRTOULL */ +#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 +/* TODO, WinRT: consider using ??_s versions of the following */ +/* #undef HAVE__STRLWR */ +/* #undef HAVE_ITOA */ +/* #undef HAVE__LTOA */ +/* #undef HAVE__ULTOA */ +/* #undef HAVE_SSCANF */ +#define HAVE_M_PI 1 +#define HAVE_ACOS 1 +#define HAVE_ACOSF 1 +#define HAVE_ASIN 1 +#define HAVE_ASINF 1 +#define HAVE_ATAN 1 +#define HAVE_ATANF 1 +#define HAVE_ATAN2 1 +#define HAVE_ATAN2F 1 +#define HAVE_CEIL 1 +#define HAVE_CEILF 1 +#define HAVE__COPYSIGN 1 +#define HAVE_COS 1 +#define HAVE_COSF 1 +#define HAVE_EXP 1 +#define HAVE_EXPF 1 +#define HAVE_FABS 1 +#define HAVE_FABSF 1 +#define HAVE_FLOOR 1 +#define HAVE_FLOORF 1 +#define HAVE_FMOD 1 +#define HAVE_FMODF 1 +#define HAVE_LOG 1 +#define HAVE_LOGF 1 +#define HAVE_LOG10 1 +#define HAVE_LOG10F 1 +#define HAVE_LROUND 1 +#define HAVE_LROUNDF 1 +#define HAVE_POW 1 +#define HAVE_POWF 1 +#define HAVE_ROUND 1 +#define HAVE_ROUNDF 1 +#define HAVE__SCALB 1 +#define HAVE_SIN 1 +#define HAVE_SINF 1 +#define HAVE_SQRT 1 +#define HAVE_SQRTF 1 +#define HAVE_TAN 1 +#define HAVE_TANF 1 +#define HAVE_TRUNC 1 +#define HAVE_TRUNCF 1 +#define HAVE__FSEEKI64 1 + +#define HAVE_ROAPI_H 1 + +/* Enable various audio drivers */ +#define SDL_AUDIO_DRIVER_WASAPI 1 +#define SDL_AUDIO_DRIVER_DISK 1 +#define SDL_AUDIO_DRIVER_DUMMY 1 + +/* Enable various input drivers */ +#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP +#define SDL_JOYSTICK_DISABLED 1 +#define SDL_HAPTIC_DISABLED 1 +#else +#define SDL_JOYSTICK_VIRTUAL 1 +#if (NTDDI_VERSION >= NTDDI_WIN10) +#define SDL_JOYSTICK_WGI 1 +#define SDL_HAPTIC_DISABLED 1 +#else +#define SDL_JOYSTICK_XINPUT 1 +#define SDL_HAPTIC_XINPUT 1 +#endif /* WIN10 */ +#endif + +/* WinRT doesn't have HIDAPI available */ +#define SDL_HIDAPI_DISABLED 1 + +/* Enable the dummy sensor driver */ +#define SDL_SENSOR_DUMMY 1 + +/* Enable various shared object loading systems */ +#define SDL_LOADSO_WINDOWS 1 + +/* Enable various threading systems */ +#if (NTDDI_VERSION >= NTDDI_WINBLUE) +#define SDL_THREAD_GENERIC_COND_SUFFIX 1 +#define SDL_THREAD_WINDOWS 1 +#else +/* WinRT on Windows 8.0 and Windows Phone 8.0 don't support CreateThread() */ +#define SDL_THREAD_STDCPP 1 +#endif + +/* 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 + +/* Enable OpenGL ES 2.0 (via a modified ANGLE library) */ +#define SDL_VIDEO_OPENGL_ES2 1 +#define SDL_VIDEO_OPENGL_EGL 1 + +/* Enable appropriate renderer(s) */ +#define SDL_VIDEO_RENDER_D3D11 1 + +/* Disable D3D12 as it's not implemented for WinRT */ +#define SDL_VIDEO_RENDER_D3D12 0 + +#if SDL_VIDEO_OPENGL_ES2 +#define SDL_VIDEO_RENDER_OGL_ES2 1 +#endif + +/* Enable system power support */ +#define SDL_POWER_WINRT 1 + +#endif /* SDL_config_winrt_h_ */ diff --git a/include/SDL_cpuinfo.h b/include/SDL_cpuinfo.h index a810d5e2a..44cbebb23 100644 --- a/include/SDL_cpuinfo.h +++ b/include/SDL_cpuinfo.h @@ -79,7 +79,7 @@ _m_prefetch(void *__P) #if !defined(SDL_DISABLE_ARM_NEON_H) # if defined(__ARM_NEON) # include -# elif defined(__WINDOWS__) || defined(__GDK__) +# elif defined(__WINDOWS__) || defined(__WINRT__) || defined(__GDK__) /* Visual Studio doesn't define __ARM_ARCH, but _M_ARM (if set, always 7), and _M_ARM64 (if set, always 1). */ # if defined(_M_ARM) # include diff --git a/include/SDL_hints.h b/include/SDL_hints.h index 11809672b..43fb5bf36 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -2112,6 +2112,105 @@ extern "C" { */ #define SDL_HINT_WINDOW_NO_ACTIVATION_WHEN_SHOWN "SDL_WINDOW_NO_ACTIVATION_WHEN_SHOWN" +/** \brief Allows back-button-press events on Windows Phone to be marked as handled + * + * Windows Phone devices typically feature a Back button. When pressed, + * the OS will emit back-button-press events, which apps are expected to + * handle in an appropriate manner. If apps do not explicitly mark these + * events as 'Handled', then the OS will invoke its default behavior for + * unhandled back-button-press events, which on Windows Phone 8 and 8.1 is to + * terminate the app (and attempt to switch to the previous app, or to the + * device's home screen). + * + * Setting the SDL_HINT_WINRT_HANDLE_BACK_BUTTON hint to "1" will cause SDL + * to mark back-button-press events as Handled, if and when one is sent to + * the app. + * + * Internally, Windows Phone sends back button events as parameters to + * special back-button-press callback functions. Apps that need to respond + * to back-button-press events are expected to register one or more + * callback functions for such, shortly after being launched (during the + * app's initialization phase). After the back button is pressed, the OS + * will invoke these callbacks. If the app's callback(s) do not explicitly + * mark the event as handled by the time they return, or if the app never + * registers one of these callback, the OS will consider the event + * un-handled, and it will apply its default back button behavior (terminate + * the app). + * + * SDL registers its own back-button-press callback with the Windows Phone + * OS. This callback will emit a pair of SDL key-press events (SDL_KEYDOWN + * and SDL_KEYUP), each with a scancode of SDL_SCANCODE_AC_BACK, after which + * it will check the contents of the hint, SDL_HINT_WINRT_HANDLE_BACK_BUTTON. + * If the hint's value is set to "1", the back button event's Handled + * property will get set to 'true'. If the hint's value is set to something + * else, or if it is unset, SDL will leave the event's Handled property + * alone. (By default, the OS sets this property to 'false', to note.) + * + * SDL apps can either set SDL_HINT_WINRT_HANDLE_BACK_BUTTON well before a + * back button is pressed, or can set it in direct-response to a back button + * being pressed. + * + * In order to get notified when a back button is pressed, SDL apps should + * register a callback function with SDL_AddEventWatch(), and have it listen + * for SDL_KEYDOWN events that have a scancode of SDL_SCANCODE_AC_BACK. + * (Alternatively, SDL_KEYUP events can be listened-for. Listening for + * either event type is suitable.) Any value of SDL_HINT_WINRT_HANDLE_BACK_BUTTON + * set by such a callback, will be applied to the OS' current + * back-button-press event. + * + * More details on back button behavior in Windows Phone apps can be found + * at the following page, on Microsoft's developer site: + * http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj247550(v=vs.105).aspx + */ +#define SDL_HINT_WINRT_HANDLE_BACK_BUTTON "SDL_WINRT_HANDLE_BACK_BUTTON" + +/** \brief Label text for a WinRT app's privacy policy link + * + * Network-enabled WinRT apps must include a privacy policy. On Windows 8, 8.1, and RT, + * Microsoft mandates that this policy be available via the Windows Settings charm. + * SDL provides code to add a link there, with its label text being set via the + * optional hint, SDL_HINT_WINRT_PRIVACY_POLICY_LABEL. + * + * Please note that a privacy policy's contents are not set via this hint. A separate + * hint, SDL_HINT_WINRT_PRIVACY_POLICY_URL, is used to link to the actual text of the + * policy. + * + * The contents of this hint should be encoded as a UTF8 string. + * + * The default value is "Privacy Policy". This hint should only be set during app + * initialization, preferably before any calls to SDL_Init(). + * + * For additional information on linking to a privacy policy, see the documentation for + * SDL_HINT_WINRT_PRIVACY_POLICY_URL. + */ +#define SDL_HINT_WINRT_PRIVACY_POLICY_LABEL "SDL_WINRT_PRIVACY_POLICY_LABEL" + +/** + * \brief A URL to a WinRT app's privacy policy + * + * All network-enabled WinRT apps must make a privacy policy available to its + * users. On Windows 8, 8.1, and RT, Microsoft mandates that this policy be + * be available in the Windows Settings charm, as accessed from within the app. + * SDL provides code to add a URL-based link there, which can point to the app's + * privacy policy. + * + * To setup a URL to an app's privacy policy, set SDL_HINT_WINRT_PRIVACY_POLICY_URL + * before calling any SDL_Init() functions. The contents of the hint should + * be a valid URL. For example, "http://www.example.com". + * + * The default value is "", which will prevent SDL from adding a privacy policy + * link to the Settings charm. This hint should only be set during app init. + * + * The label text of an app's "Privacy Policy" link may be customized via another + * hint, SDL_HINT_WINRT_PRIVACY_POLICY_LABEL. + * + * Please note that on Windows Phone, Microsoft does not provide standard UI + * for displaying a privacy policy link, and as such, SDL_HINT_WINRT_PRIVACY_POLICY_URL + * will not get used on that platform. Network-enabled phone apps should display + * their privacy policy through some other, in-app means. + */ +#define SDL_HINT_WINRT_PRIVACY_POLICY_URL "SDL_WINRT_PRIVACY_POLICY_URL" + /** * \brief Mark X11 windows as override-redirect. * diff --git a/include/SDL_main.h b/include/SDL_main.h index 01dfb2669..4553b4f07 100644 --- a/include/SDL_main.h +++ b/include/SDL_main.h @@ -39,6 +39,18 @@ */ #define SDL_MAIN_AVAILABLE +#elif defined(__WINRT__) +/* On WinRT, SDL provides a main function that initializes CoreApplication, + creating an instance of IFrameworkView in the process. + + Please note that #include'ing SDL_main.h is not enough to get a main() + function working. In non-XAML apps, the file, + src/main/winrt/SDL_WinRT_main_NonXAML.cpp, or a copy of it, must be compiled + into the app itself. In XAML apps, the function, SDL_WinRTRunApp must be + called, with a pointer to the Direct3D-hosted XAML control passed in. +*/ +#define SDL_MAIN_NEEDED + #elif defined(__GDK__) /* On GDK, SDL provides a main function that initializes the game runtime. @@ -196,6 +208,22 @@ extern DECLSPEC void SDLCALL SDL_UnregisterApp(void); #endif /* defined(__WIN32__) || defined(__GDK__) */ +#ifdef __WINRT__ + +/** + * Initialize and launch an SDL/WinRT application. + * + * \param mainFunction the SDL app's C-style main(), an SDL_main_func + * \param reserved reserved for future use; should be NULL + * \returns 0 on success or -1 on failure; call SDL_GetError() to retrieve + * more information on the failure. + * + * \since This function is available since SDL 2.0.3. + */ +extern DECLSPEC int SDLCALL SDL_WinRTRunApp(SDL_main_func mainFunction, void * reserved); + +#endif /* __WINRT__ */ + #if defined(__IPHONEOS__) /** diff --git a/include/SDL_platform.h b/include/SDL_platform.h index af9e9558f..832a48743 100644 --- a/include/SDL_platform.h +++ b/include/SDL_platform.h @@ -167,7 +167,8 @@ #endif /* HAVE_WINAPIFAMILY_H */ #if WINAPI_FAMILY_WINRT -#error WinRT no longer supported. +#undef __WINRT__ +#define __WINRT__ 1 #elif defined(_GAMING_DESKTOP) /* GDK project configuration always defines _GAMING_XXX */ #undef __WINGDK__ #define __WINGDK__ 1 diff --git a/include/SDL_system.h b/include/SDL_system.h index d70f9a6a9..0cf73858b 100644 --- a/include/SDL_system.h +++ b/include/SDL_system.h @@ -466,6 +466,109 @@ extern DECLSPEC int SDLCALL SDL_AndroidSendMessage(Uint32 command, int param); #endif /* __ANDROID__ */ +/* Platform specific functions for WinRT */ +#ifdef __WINRT__ + +/** + * \brief WinRT / 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 WinRT Device Family + */ +typedef enum +{ + /** \brief Unknown family */ + SDL_WINRT_DEVICEFAMILY_UNKNOWN, + + /** \brief Desktop family*/ + SDL_WINRT_DEVICEFAMILY_DESKTOP, + + /** \brief Mobile family (for example smartphone) */ + SDL_WINRT_DEVICEFAMILY_MOBILE, + + /** \brief XBox family */ + SDL_WINRT_DEVICEFAMILY_XBOX, +} SDL_WinRT_DeviceFamily; + + +/** + * Retrieve a WinRT defined path on the local file system. + * + * 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. + * + * Documentation on most app-specific path types on WinRT can be found on + * MSDN, at the URL: + * + * https://msdn.microsoft.com/en-us/library/windows/apps/hh464917.aspx + * + * \param pathType the type of path to retrieve, one of SDL_WinRT_Path + * \returns a UCS-2 string (16-bit, wide-char) containing the path, or NULL if + * the path is not available for any reason; call SDL_GetError() for + * more information. + * + * \since This function is available since SDL 2.0.3. + * + * \sa SDL_WinRTGetFSPathUTF8 + */ +extern DECLSPEC const wchar_t * SDLCALL SDL_WinRTGetFSPathUNICODE(SDL_WinRT_Path pathType); + +/** + * Retrieve a WinRT defined path on the local file system. + * + * 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. + * + * Documentation on most app-specific path types on WinRT can be found on + * MSDN, at the URL: + * + * https://msdn.microsoft.com/en-us/library/windows/apps/hh464917.aspx + * + * \param pathType the type of path to retrieve, one of SDL_WinRT_Path + * \returns a UTF-8 string (8-bit, multi-byte) containing the path, or NULL if + * the path is not available for any reason; call SDL_GetError() for + * more information. + * + * \since This function is available since SDL 2.0.3. + * + * \sa SDL_WinRTGetFSPathUNICODE + */ +extern DECLSPEC const char * SDLCALL SDL_WinRTGetFSPathUTF8(SDL_WinRT_Path pathType); + +/** + * Detects the device family of WinRT platform at runtime. + * + * \returns a value from the SDL_WinRT_DeviceFamily enum. + * + * \since This function is available since SDL 2.0.8. + */ +extern DECLSPEC SDL_WinRT_DeviceFamily SDLCALL SDL_WinRTGetDeviceFamily(); + +#endif /* __WINRT__ */ + /** * Query if the current device is a tablet. * diff --git a/include/SDL_syswm.h b/include/SDL_syswm.h index 820f2a700..16ff40892 100644 --- a/include/SDL_syswm.h +++ b/include/SDL_syswm.h @@ -55,6 +55,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__) @@ -128,6 +132,7 @@ typedef enum SDL_SYSWM_COCOA, SDL_SYSWM_UIKIT, SDL_SYSWM_WAYLAND, + SDL_SYSWM_WINRT, SDL_SYSWM_ANDROID, SDL_SYSWM_VIVANTE, SDL_SYSWM_HAIKU, @@ -206,6 +211,12 @@ struct SDL_SysWMinfo HINSTANCE hinstance; /**< The instance handle */ } win; #endif +#if defined(SDL_VIDEO_DRIVER_WINRT) + struct + { + IInspectable * window; /**< The WinRT CoreWindow */ + } winrt; +#endif #if defined(SDL_VIDEO_DRIVER_X11) struct { diff --git a/include/begin_code.h b/include/begin_code.h index 397026b97..5bdcb1acc 100644 --- a/include/begin_code.h +++ b/include/begin_code.h @@ -51,7 +51,7 @@ /* Some compilers use a special export keyword */ #ifndef DECLSPEC -# if defined(__WIN32__) || defined(__CYGWIN__) || defined(__GDK__) +# if defined(__WIN32__) || defined(__WINRT__) || defined(__CYGWIN__) || defined(__GDK__) # ifdef DLL_EXPORT # define DECLSPEC __declspec(dllexport) # else @@ -68,7 +68,7 @@ /* By default SDL uses the C calling convention */ #ifndef SDLCALL -#if (defined(__WIN32__) || defined(__GDK__)) && !defined(__GNUC__) +#if (defined(__WIN32__) || defined(__WINRT__) || defined(__GDK__)) && !defined(__GNUC__) #define SDLCALL __cdecl #else #define SDLCALL diff --git a/src/SDL.c b/src/SDL.c index 9dccfe3fa..90bf7a2cb 100644 --- a/src/SDL.c +++ b/src/SDL.c @@ -22,7 +22,7 @@ #if defined(__WIN32__) || defined(__GDK__) #include "core/windows/SDL_windows.h" -#else +#elif !defined(__WINRT__) #include /* _exit(), etc. */ #endif @@ -575,6 +575,8 @@ SDL_GetPlatform(void) return "Solaris"; #elif __WIN32__ return "Windows"; +#elif __WINRT__ + return "WinRT"; #elif __WINGDK__ return "WinGDK"; #elif __XBOXONE__ diff --git a/src/SDL_log.c b/src/SDL_log.c index b9498e91d..9a407fd72 100644 --- a/src/SDL_log.c +++ b/src/SDL_log.c @@ -20,7 +20,7 @@ */ #include "./SDL_internal.h" -#if defined(__WIN32__) || defined(__GDK__) +#if defined(__WIN32__) || defined(__WINRT__) || defined(__GDK__) #include "core/windows/SDL_windows.h" #endif @@ -366,7 +366,7 @@ SDL_LogMessageV(int category, SDL_LogPriority priority, const char *fmt, va_list } } -#if defined(__WIN32__) && !defined(HAVE_STDIO_H) && !defined(__GDK__) +#if defined(__WIN32__) && !defined(HAVE_STDIO_H) && !defined(__WINRT__) && !defined(__GDK__) /* Flag tracking the attachment of the console: 0=unattached, 1=attached to a console, 2=attached to a file, -1=error */ static int consoleAttached = 0; @@ -378,7 +378,7 @@ static void SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, const char *message) { -#if defined(__WIN32__) || defined(__GDK__) +#if defined(__WIN32__) || defined(__WINRT__) || defined(__GDK__) /* Way too many allocations here, urgh */ /* Note: One can't call SDL_SetError here, since that function itself logs. */ { @@ -387,7 +387,7 @@ SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, LPTSTR tstr; SDL_bool isstack; -#if !defined(HAVE_STDIO_H) && !defined(__GDK__) +#if !defined(HAVE_STDIO_H) && !defined(__WINRT__) && !defined(__GDK__) BOOL attachResult; DWORD attachError; DWORD charsWritten; @@ -426,7 +426,7 @@ SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, } } } -#endif /* !defined(HAVE_STDIO_H) && !defined(__GDK__) */ +#endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) && !defined(__GDK__) */ length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 1 + 1 + 1; output = SDL_small_alloc(char, length, &isstack); @@ -436,7 +436,7 @@ SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, /* Output to debugger */ OutputDebugString(tstr); -#if !defined(HAVE_STDIO_H) && !defined(__GDK__) +#if !defined(HAVE_STDIO_H) && !defined(__WINRT__) && !defined(__GDK__) /* Screen output to stderr, if console was attached. */ if (consoleAttached == 1) { if (!WriteConsole(stderrHandle, tstr, (DWORD) SDL_tcslen(tstr), &charsWritten, NULL)) { @@ -451,7 +451,7 @@ SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, OutputDebugString(TEXT("Error calling WriteFile\r\n")); } } -#endif /* !defined(HAVE_STDIO_H) && !defined(__GDK__) */ +#endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) && !defined(__GDK__) */ SDL_free(tstr); SDL_small_free(output, isstack); diff --git a/src/atomic/SDL_spinlock.c b/src/atomic/SDL_spinlock.c index 2508d02a6..965c021e1 100644 --- a/src/atomic/SDL_spinlock.c +++ b/src/atomic/SDL_spinlock.c @@ -20,7 +20,7 @@ */ #include "../SDL_internal.h" -#if defined(__WIN32__) || defined(__GDK__) +#if defined(__WIN32__) || defined(__WINRT__) || defined(__GDK__) #include "../core/windows/SDL_windows.h" #endif diff --git a/src/audio/wasapi/SDL_wasapi.c b/src/audio/wasapi/SDL_wasapi.c index bbb8d0180..3a1acfeb0 100644 --- a/src/audio/wasapi/SDL_wasapi.c +++ b/src/audio/wasapi/SDL_wasapi.c @@ -46,27 +46,13 @@ #endif /* Some GUIDs we need to know without linking to libraries that aren't available before Vista. */ -static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32,{ 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } }; static const IID SDL_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483,{ 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } }; static const IID SDL_IID_IAudioCaptureClient = { 0xc8adbd64, 0xe71e, 0x48a0,{ 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c, 0xd3, 0x17 } }; -/* handle to Avrt.dll--Vista and later!--for flagging the callback thread as "Pro Audio" (low latency). */ -static HMODULE libavrt = NULL; -typedef HANDLE(WINAPI *pfnAvSetMmThreadCharacteristicsW)(LPCWSTR, LPDWORD); -typedef BOOL(WINAPI *pfnAvRevertMmThreadCharacteristics)(HANDLE); -static pfnAvSetMmThreadCharacteristicsW pAvSetMmThreadCharacteristicsW = NULL; -static pfnAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NULL; - static void WASAPI_DetectDevices(void) { - SDL_IMMDevice_EnumerateEndpoints(SDL_FALSE); -} - -int -WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture) -{ - return SDL_IMMDevice_GetDefaultAudioInfo(name, spec, iscapture); + WASAPI_EnumerateEndpoints(); } static SDL_INLINE SDL_bool @@ -140,7 +126,7 @@ UpdateAudioStream(_THIS, const SDL_AudioSpec *oldspec) return 0; } -static int ActivateWasapiDevice(_THIS, SDL_bool isrecovery); + static void ReleaseWasapiDevice(_THIS); static SDL_bool @@ -157,7 +143,7 @@ RecoverWasapiDevice(_THIS) devices try to reinitialize whatever the new default is, so it's more likely to carry on here, but this handles a non-default device that simply had its format changed in the Windows Control Panel. */ - if (ActivateWasapiDevice(this, SDL_TRUE) == -1) { + if (WASAPI_ActivateDevice(this, SDL_TRUE) == -1) { SDL_OpenedAudioDeviceDisconnected(this); return SDL_FALSE; } @@ -363,6 +349,11 @@ ReleaseWasapiDevice(_THIS) this->hidden->capturestream = NULL; } + if (this->hidden->activation_handler) { + WASAPI_PlatformDeleteActivationHandler(this->hidden->activation_handler); + this->hidden->activation_handler = NULL; + } + if (this->hidden->event) { CloseHandle(this->hidden->event); this->hidden->event = NULL; @@ -371,6 +362,18 @@ ReleaseWasapiDevice(_THIS) static void WASAPI_CloseDevice(_THIS) +{ + WASAPI_UnrefDevice(this); +} + +void +WASAPI_RefDevice(_THIS) +{ + SDL_AtomicIncRef(&this->hidden->refcount); +} + +void +WASAPI_UnrefDevice(_THIS) { if (!SDL_AtomicDecRef(&this->hidden->refcount)) { return; @@ -387,7 +390,7 @@ WASAPI_CloseDevice(_THIS) } /* This is called once a device is activated, possibly asynchronously. */ -static int +int WASAPI_PrepDevice(_THIS, const SDL_bool updatestream) { /* !!! FIXME: we could request an exclusive mode stream, which is lower latency; @@ -416,7 +419,7 @@ WASAPI_PrepDevice(_THIS, const SDL_bool updatestream) SDL_assert(client != NULL); -#if defined(__GDK__) /* CreateEventEx() arrived in Vista, so we need an #ifdef for XP. */ +#if defined(__WINRT__) || defined(__GDK__) /* CreateEventEx() arrived in Vista, so we need an #ifdef for XP. */ this->hidden->event = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS); #else this->hidden->event = CreateEventW(NULL, 0, 0, NULL); @@ -538,34 +541,6 @@ WASAPI_PrepDevice(_THIS, const SDL_bool updatestream) return 0; /* good to go. */ } -static int -ActivateWasapiDevice(_THIS, const SDL_bool isrecovery) -{ - IMMDevice *device = NULL; - HRESULT ret; - - if (SDL_IMMDevice_Get(this->hidden->devid, &device, this->iscapture) < 0) { - this->hidden->client = NULL; - return -1; /* This is already set by SDL_IMMDevice_Get */ - } - - /* this is not async in standard win32, yay! */ - ret = IMMDevice_Activate(device, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **) &this->hidden->client); - IMMDevice_Release(device); - - if (FAILED(ret)) { - SDL_assert(this->hidden->client == NULL); - return WIN_SetErrorFromHRESULT("WASAPI can't activate audio endpoint", ret); - } - - SDL_assert(this->hidden->client != NULL); - if (WASAPI_PrepDevice(this, isrecovery) == -1) { /* not async, fire it right away. */ - return -1; - } - - return 0; /* good to go. */ -} - static int WASAPI_OpenDevice(_THIS, const char *devname) @@ -580,7 +555,7 @@ WASAPI_OpenDevice(_THIS, const char *devname) } SDL_zerop(this->hidden); - SDL_AtomicIncRef(&this->hidden->refcount); /* so CloseDevice() will unref to zero. */ + WASAPI_RefDevice(this); /* so CloseDevice() will unref to zero. */ if (!devid) { /* is default device? */ this->hidden->default_device_generation = SDL_AtomicGet(this->iscapture ? &SDL_IMMDevice_DefaultCaptureGeneration : &SDL_IMMDevice_DefaultPlaybackGeneration); @@ -591,7 +566,7 @@ WASAPI_OpenDevice(_THIS, const char *devname) } } - if (ActivateWasapiDevice(this, SDL_FALSE) == -1) { + if (WASAPI_ActivateDevice(this, SDL_FALSE) == -1) { return -1; /* already set error. */ } @@ -609,58 +584,26 @@ WASAPI_OpenDevice(_THIS, const char *devname) static void WASAPI_ThreadInit(_THIS) { - /* this thread uses COM. */ - if (SUCCEEDED(WIN_CoInitialize())) { /* can't report errors, hope it worked! */ - this->hidden->coinitialized = SDL_TRUE; - } - - /* Set this thread to very high "Pro Audio" priority. */ - if (pAvSetMmThreadCharacteristicsW) { - DWORD idx = 0; - this->hidden->task = pAvSetMmThreadCharacteristicsW(L"Pro Audio", &idx); - } + WASAPI_PlatformThreadInit(this); } static void WASAPI_ThreadDeinit(_THIS) { - /* Set this thread back to normal priority. */ - if (this->hidden->task && pAvRevertMmThreadCharacteristics) { - pAvRevertMmThreadCharacteristics(this->hidden->task); - this->hidden->task = NULL; - } - - if (this->hidden->coinitialized) { - WIN_CoUninitialize(); - this->hidden->coinitialized = SDL_FALSE; - } + WASAPI_PlatformThreadDeinit(this); } static void WASAPI_Deinitialize(void) { - if (libavrt) { - FreeLibrary(libavrt); - libavrt = NULL; - } - - pAvSetMmThreadCharacteristicsW = NULL; - pAvRevertMmThreadCharacteristics = NULL; - - SDL_IMMDevice_Quit(); + WASAPI_PlatformDeinit(); } static SDL_bool WASAPI_Init(SDL_AudioDriverImpl * impl) { - if (SDL_IMMDevice_Init() < 0) { - return SDL_FALSE; /* Error is set by SDL_IMMDevice_Init */ - } - - libavrt = LoadLibrary(TEXT("avrt.dll")); /* this library is available in Vista and later. No WinXP, so have to LoadLibrary to use it for now! */ - if (libavrt) { - pAvSetMmThreadCharacteristicsW = (pfnAvSetMmThreadCharacteristicsW) GetProcAddress(libavrt, "AvSetMmThreadCharacteristicsW"); - pAvRevertMmThreadCharacteristics = (pfnAvRevertMmThreadCharacteristics) GetProcAddress(libavrt, "AvRevertMmThreadCharacteristics"); + if (WASAPI_PlatformInit() == -1) { + return SDL_FALSE; } /* Set the function pointers */ diff --git a/src/audio/wasapi/SDL_wasapi.h b/src/audio/wasapi/SDL_wasapi.h index 4994bd710..69060677d 100644 --- a/src/audio/wasapi/SDL_wasapi.h +++ b/src/audio/wasapi/SDL_wasapi.h @@ -51,8 +51,25 @@ struct SDL_PrivateAudioData int framesize; int default_device_generation; SDL_bool device_lost; + void *activation_handler; + SDL_atomic_t just_activated; }; +/* win32 and winrt implementations call into these. */ +int WASAPI_PrepDevice(_THIS, const SDL_bool updatestream); +void WASAPI_RefDevice(_THIS); +void WASAPI_UnrefDevice(_THIS); + +/* These are functions that are implemented differently for Windows vs WinRT. */ +int WASAPI_PlatformInit(void); +void WASAPI_PlatformDeinit(void); +void WASAPI_EnumerateEndpoints(void); +int WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture); +int WASAPI_ActivateDevice(_THIS, const SDL_bool isrecovery); +void WASAPI_PlatformThreadInit(_THIS); +void WASAPI_PlatformThreadDeinit(_THIS); +void WASAPI_PlatformDeleteActivationHandler(void *handler); + #ifdef __cplusplus } #endif diff --git a/src/audio/wasapi/SDL_wasapi_win32.c b/src/audio/wasapi/SDL_wasapi_win32.c new file mode 100644 index 000000000..13f05b661 --- /dev/null +++ b/src/audio/wasapi/SDL_wasapi_win32.c @@ -0,0 +1,162 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_internal.h" + +/* This is code that Windows uses to talk to WASAPI-related system APIs. + This is for non-WinRT desktop apps. The C++/CX implementation of these + functions, exclusive to WinRT, are in SDL_wasapi_winrt.cpp. + The code in SDL_wasapi.c is used by both standard Windows and WinRT builds + to deal with audio and calls into these functions. */ + +#if SDL_AUDIO_DRIVER_WASAPI && !defined(__WINRT__) + +#include "../../core/windows/SDL_windows.h" +#include "../../core/windows/SDL_immdevice.h" +#include "SDL_audio.h" +#include "SDL_timer.h" +#include "../SDL_audio_c.h" +#include "../SDL_sysaudio.h" + +#include + +#include "SDL_wasapi.h" + +/* handle to Avrt.dll--Vista and later!--for flagging the callback thread as "Pro Audio" (low latency). */ +static HMODULE libavrt = NULL; +typedef HANDLE(WINAPI *pfnAvSetMmThreadCharacteristicsW)(LPCWSTR, LPDWORD); +typedef BOOL(WINAPI *pfnAvRevertMmThreadCharacteristics)(HANDLE); +static pfnAvSetMmThreadCharacteristicsW pAvSetMmThreadCharacteristicsW = NULL; +static pfnAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NULL; + +/* Some GUIDs we need to know without linking to libraries that aren't available before Vista. */ +static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32,{ 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } }; + +int +WASAPI_PlatformInit(void) +{ + if (SDL_IMMDevice_Init() < 0) { + return -1; /* This is set by SDL_IMMDevice_Init */ + } + + libavrt = LoadLibrary(TEXT("avrt.dll")); /* this library is available in Vista and later. No WinXP, so have to LoadLibrary to use it for now! */ + if (libavrt) { + pAvSetMmThreadCharacteristicsW = (pfnAvSetMmThreadCharacteristicsW) GetProcAddress(libavrt, "AvSetMmThreadCharacteristicsW"); + pAvRevertMmThreadCharacteristics = (pfnAvRevertMmThreadCharacteristics) GetProcAddress(libavrt, "AvRevertMmThreadCharacteristics"); + } + + return 0; +} + +void +WASAPI_PlatformDeinit(void) +{ + if (libavrt) { + FreeLibrary(libavrt); + libavrt = NULL; + } + + pAvSetMmThreadCharacteristicsW = NULL; + pAvRevertMmThreadCharacteristics = NULL; + + SDL_IMMDevice_Quit(); +} + +void +WASAPI_PlatformThreadInit(_THIS) +{ + /* this thread uses COM. */ + if (SUCCEEDED(WIN_CoInitialize())) { /* can't report errors, hope it worked! */ + this->hidden->coinitialized = SDL_TRUE; + } + + /* Set this thread to very high "Pro Audio" priority. */ + if (pAvSetMmThreadCharacteristicsW) { + DWORD idx = 0; + this->hidden->task = pAvSetMmThreadCharacteristicsW(L"Pro Audio", &idx); + } +} + +void +WASAPI_PlatformThreadDeinit(_THIS) +{ + /* Set this thread back to normal priority. */ + if (this->hidden->task && pAvRevertMmThreadCharacteristics) { + pAvRevertMmThreadCharacteristics(this->hidden->task); + this->hidden->task = NULL; + } + + if (this->hidden->coinitialized) { + WIN_CoUninitialize(); + this->hidden->coinitialized = SDL_FALSE; + } +} + +int +WASAPI_ActivateDevice(_THIS, const SDL_bool isrecovery) +{ + IMMDevice *device = NULL; + HRESULT ret; + + if (SDL_IMMDevice_Get(this->hidden->devid, &device, this->iscapture) < 0) { + this->hidden->client = NULL; + return -1; /* This is already set by SDL_IMMDevice_Get */ + } + + /* this is not async in standard win32, yay! */ + ret = IMMDevice_Activate(device, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **) &this->hidden->client); + IMMDevice_Release(device); + + if (FAILED(ret)) { + SDL_assert(this->hidden->client == NULL); + return WIN_SetErrorFromHRESULT("WASAPI can't activate audio endpoint", ret); + } + + SDL_assert(this->hidden->client != NULL); + if (WASAPI_PrepDevice(this, isrecovery) == -1) { /* not async, fire it right away. */ + return -1; + } + + return 0; /* good to go. */ +} + +void +WASAPI_EnumerateEndpoints(void) +{ + SDL_IMMDevice_EnumerateEndpoints(SDL_FALSE); +} + +int +WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture) +{ + return SDL_IMMDevice_GetDefaultAudioInfo(name, spec, iscapture); +} + +void +WASAPI_PlatformDeleteActivationHandler(void *handler) +{ + /* not asynchronous. */ + SDL_assert(!"This function should have only been called on WinRT."); +} + +#endif /* SDL_AUDIO_DRIVER_WASAPI && !defined(__WINRT__) */ + +/* vi: set ts=4 sw=4 expandtab: */ + diff --git a/src/audio/wasapi/SDL_wasapi_winrt.cpp b/src/audio/wasapi/SDL_wasapi_winrt.cpp new file mode 100644 index 000000000..235ae7d32 --- /dev/null +++ b/src/audio/wasapi/SDL_wasapi_winrt.cpp @@ -0,0 +1,447 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_internal.h" + +// This is C++/CX code that the WinRT port uses to talk to WASAPI-related +// system APIs. The C implementation of these functions, for non-WinRT apps, +// is in SDL_wasapi_win32.c. The code in SDL_wasapi.c is used by both standard +// Windows and WinRT builds to deal with audio and calls into these functions. + +#if SDL_AUDIO_DRIVER_WASAPI && defined(__WINRT__) + +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "../../core/windows/SDL_windows.h" +#include "SDL_audio.h" +#include "SDL_timer.h" +#include "../SDL_audio_c.h" +#include "../SDL_sysaudio.h" +} + +#define COBJMACROS +#include +#include + +#include "SDL_wasapi.h" + +using namespace Windows::Devices::Enumeration; +using namespace Windows::Media::Devices; +using namespace Windows::Foundation; +using namespace Microsoft::WRL; + +static Platform::String^ SDL_PKEY_AudioEngine_DeviceFormat = L"{f19f064d-082c-4e27-bc73-6882a1bb8e4c} 0"; + +static void WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid); +static void WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid); +extern "C" { + SDL_atomic_t SDL_IMMDevice_DefaultPlaybackGeneration; + SDL_atomic_t SDL_IMMDevice_DefaultCaptureGeneration; +} + +/* This is a list of device id strings we have inflight, so we have consistent pointers to the same device. */ +typedef struct DevIdList +{ + WCHAR *str; + struct DevIdList *next; +} DevIdList; + +static DevIdList *deviceid_list = NULL; + +class SDL_WasapiDeviceEventHandler +{ +public: + SDL_WasapiDeviceEventHandler(const SDL_bool _iscapture); + ~SDL_WasapiDeviceEventHandler(); + void OnDeviceAdded(DeviceWatcher^ sender, DeviceInformation^ args); + void OnDeviceRemoved(DeviceWatcher^ sender, DeviceInformationUpdate^ args); + void OnDeviceUpdated(DeviceWatcher^ sender, DeviceInformationUpdate^ args); + void OnEnumerationCompleted(DeviceWatcher^ sender, Platform::Object^ args); + void OnDefaultRenderDeviceChanged(Platform::Object^ sender, DefaultAudioRenderDeviceChangedEventArgs^ args); + void OnDefaultCaptureDeviceChanged(Platform::Object^ sender, DefaultAudioCaptureDeviceChangedEventArgs^ args); + SDL_semaphore* completed; + +private: + const SDL_bool iscapture; + DeviceWatcher^ watcher; + Windows::Foundation::EventRegistrationToken added_handler; + Windows::Foundation::EventRegistrationToken removed_handler; + Windows::Foundation::EventRegistrationToken updated_handler; + Windows::Foundation::EventRegistrationToken completed_handler; + Windows::Foundation::EventRegistrationToken default_changed_handler; +}; + +SDL_WasapiDeviceEventHandler::SDL_WasapiDeviceEventHandler(const SDL_bool _iscapture) + : iscapture(_iscapture) + , completed(SDL_CreateSemaphore(0)) +{ + if (!completed) + return; // uhoh. + + Platform::String^ selector = _iscapture ? MediaDevice::GetAudioCaptureSelector() : + MediaDevice::GetAudioRenderSelector(); + Platform::Collections::Vector properties; + properties.Append(SDL_PKEY_AudioEngine_DeviceFormat); + watcher = DeviceInformation::CreateWatcher(selector, properties.GetView()); + if (!watcher) + return; // uhoh. + + // !!! FIXME: this doesn't need a lambda here, I think, if I make SDL_WasapiDeviceEventHandler a proper C++/CX class. --ryan. + added_handler = watcher->Added += ref new TypedEventHandler([this](DeviceWatcher^ sender, DeviceInformation^ args) { OnDeviceAdded(sender, args); } ); + removed_handler = watcher->Removed += ref new TypedEventHandler([this](DeviceWatcher^ sender, DeviceInformationUpdate^ args) { OnDeviceRemoved(sender, args); } ); + updated_handler = watcher->Updated += ref new TypedEventHandler([this](DeviceWatcher^ sender, DeviceInformationUpdate^ args) { OnDeviceUpdated(sender, args); } ); + completed_handler = watcher->EnumerationCompleted += ref new TypedEventHandler([this](DeviceWatcher^ sender, Platform::Object^ args) { OnEnumerationCompleted(sender, args); } ); + if (iscapture) { + default_changed_handler = MediaDevice::DefaultAudioCaptureDeviceChanged += ref new TypedEventHandler([this](Platform::Object^ sender, DefaultAudioCaptureDeviceChangedEventArgs^ args) { OnDefaultCaptureDeviceChanged(sender, args); } ); + } else { + default_changed_handler = MediaDevice::DefaultAudioRenderDeviceChanged += ref new TypedEventHandler([this](Platform::Object^ sender, DefaultAudioRenderDeviceChangedEventArgs^ args) { OnDefaultRenderDeviceChanged(sender, args); } ); + } + watcher->Start(); +} + +SDL_WasapiDeviceEventHandler::~SDL_WasapiDeviceEventHandler() +{ + if (watcher) { + watcher->Added -= added_handler; + watcher->Removed -= removed_handler; + watcher->Updated -= updated_handler; + watcher->EnumerationCompleted -= completed_handler; + watcher->Stop(); + watcher = nullptr; + } + if (completed) { + SDL_DestroySemaphore(completed); + completed = nullptr; + } + + if (iscapture) { + MediaDevice::DefaultAudioCaptureDeviceChanged -= default_changed_handler; + } else { + MediaDevice::DefaultAudioRenderDeviceChanged -= default_changed_handler; + } +} + +void +SDL_WasapiDeviceEventHandler::OnDeviceAdded(DeviceWatcher^ sender, DeviceInformation^ info) +{ + SDL_assert(sender == this->watcher); + char *utf8dev = WIN_StringToUTF8(info->Name->Data()); + if (utf8dev) { + WAVEFORMATEXTENSIBLE fmt; + Platform::Object^ obj = info->Properties->Lookup(SDL_PKEY_AudioEngine_DeviceFormat); + if (obj) { + IPropertyValue^ property = (IPropertyValue^) obj; + Platform::Array^ data; + property->GetUInt8Array(&data); + SDL_memcpy(&fmt, data->Data, SDL_min(data->Length, sizeof(WAVEFORMATEXTENSIBLE))); + } else { + SDL_zero(fmt); + } + + WASAPI_AddDevice(this->iscapture, utf8dev, &fmt, info->Id->Data()); + SDL_free(utf8dev); + } +} + +void +SDL_WasapiDeviceEventHandler::OnDeviceRemoved(DeviceWatcher^ sender, DeviceInformationUpdate^ info) +{ + SDL_assert(sender == this->watcher); + WASAPI_RemoveDevice(this->iscapture, info->Id->Data()); +} + +void +SDL_WasapiDeviceEventHandler::OnDeviceUpdated(DeviceWatcher^ sender, DeviceInformationUpdate^ args) +{ + SDL_assert(sender == this->watcher); +} + +void +SDL_WasapiDeviceEventHandler::OnEnumerationCompleted(DeviceWatcher^ sender, Platform::Object^ args) +{ + SDL_assert(sender == this->watcher); + SDL_SemPost(this->completed); +} + +void +SDL_WasapiDeviceEventHandler::OnDefaultRenderDeviceChanged(Platform::Object^ sender, DefaultAudioRenderDeviceChangedEventArgs^ args) +{ + SDL_assert(this->iscapture); + SDL_AtomicAdd(&SDL_IMMDevice_DefaultPlaybackGeneration, 1); +} + +void +SDL_WasapiDeviceEventHandler::OnDefaultCaptureDeviceChanged(Platform::Object^ sender, DefaultAudioCaptureDeviceChangedEventArgs^ args) +{ + SDL_assert(!this->iscapture); + SDL_AtomicAdd(&SDL_IMMDevice_DefaultCaptureGeneration, 1); +} + + +static SDL_WasapiDeviceEventHandler *playback_device_event_handler; +static SDL_WasapiDeviceEventHandler *capture_device_event_handler; + +int WASAPI_PlatformInit(void) +{ + SDL_AtomicSet(&SDL_IMMDevice_DefaultPlaybackGeneration, 1); + SDL_AtomicSet(&SDL_IMMDevice_DefaultCaptureGeneration, 1); + return 0; +} + +void WASAPI_PlatformDeinit(void) +{ + DevIdList *devidlist; + DevIdList *next; + + delete playback_device_event_handler; + playback_device_event_handler = nullptr; + delete capture_device_event_handler; + capture_device_event_handler = nullptr; + + for (devidlist = deviceid_list; devidlist; devidlist = next) { + next = devidlist->next; + SDL_free(devidlist->str); + SDL_free(devidlist); + } + deviceid_list = NULL; +} + +void WASAPI_EnumerateEndpoints(void) +{ + // DeviceWatchers will fire an Added event for each existing device at + // startup, so we don't need to enumerate them separately before + // listening for updates. + playback_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_FALSE); + capture_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_TRUE); + SDL_SemWait(playback_device_event_handler->completed); + SDL_SemWait(capture_device_event_handler->completed); +} + +struct SDL_WasapiActivationHandler : public RuntimeClass< RuntimeClassFlags< ClassicCom >, FtmBase, IActivateAudioInterfaceCompletionHandler > +{ + SDL_WasapiActivationHandler() : device(nullptr) {} + STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation *operation); + SDL_AudioDevice *device; +}; + +HRESULT +SDL_WasapiActivationHandler::ActivateCompleted(IActivateAudioInterfaceAsyncOperation *async) +{ + // Just set a flag, since we're probably in a different thread. We'll pick it up and init everything on our own thread to prevent races. + SDL_AtomicSet(&device->hidden->just_activated, 1); + WASAPI_UnrefDevice(device); + return S_OK; +} + +void +WASAPI_PlatformDeleteActivationHandler(void *handler) +{ + ((SDL_WasapiActivationHandler *) handler)->Release(); +} + +int +WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture) +{ + return SDL_Unsupported(); +} + +int +WASAPI_ActivateDevice(_THIS, const SDL_bool isrecovery) +{ + LPCWSTR devid = _this->hidden->devid; + Platform::String^ defdevid; + + if (devid == nullptr) { + defdevid = _this->iscapture ? MediaDevice::GetDefaultAudioCaptureId(AudioDeviceRole::Default) : MediaDevice::GetDefaultAudioRenderId(AudioDeviceRole::Default); + if (defdevid) { + devid = defdevid->Data(); + } + } + + SDL_AtomicSet(&_this->hidden->just_activated, 0); + + ComPtr handler = Make(); + if (handler == nullptr) { + return SDL_SetError("Failed to allocate WASAPI activation handler"); + } + + handler.Get()->AddRef(); // we hold a reference after ComPtr destructs on return, causing a Release, and Release ourselves in WASAPI_PlatformDeleteActivationHandler(), etc. + handler.Get()->device = _this; + _this->hidden->activation_handler = handler.Get(); + + WASAPI_RefDevice(_this); /* completion handler will unref it. */ + IActivateAudioInterfaceAsyncOperation *async = nullptr; + const HRESULT ret = ActivateAudioInterfaceAsync(devid, __uuidof(IAudioClient), nullptr, handler.Get(), &async); + + if (FAILED(ret) || async == nullptr) { + if (async != nullptr) { + async->Release(); + } + handler.Get()->Release(); + WASAPI_UnrefDevice(_this); + return WIN_SetErrorFromHRESULT("WASAPI can't activate requested audio endpoint", ret); + } + + /* Spin until the async operation is complete. + * If we don't PrepDevice before leaving this function, the bug list gets LONG: + * - device.spec is not filled with the correct information + * - The 'obtained' spec will be wrong for ALLOW_CHANGE properties + * - SDL_AudioStreams will/will not be allocated at the right time + * - SDL_assert(device->callbackspec.size == device->spec.size) will fail + * - When the assert is ignored, skipping or a buffer overflow will occur + */ + while (!SDL_AtomicCAS(&_this->hidden->just_activated, 1, 0)) { + SDL_Delay(1); + } + + HRESULT activateRes = S_OK; + IUnknown *iunknown = nullptr; + const HRESULT getActivateRes = async->GetActivateResult(&activateRes, &iunknown); + async->Release(); + if (FAILED(getActivateRes)) { + return WIN_SetErrorFromHRESULT("Failed to get WASAPI activate result", getActivateRes); + } else if (FAILED(activateRes)) { + return WIN_SetErrorFromHRESULT("Failed to activate WASAPI device", activateRes); + } + + iunknown->QueryInterface(IID_PPV_ARGS(&_this->hidden->client)); + if (!_this->hidden->client) { + return SDL_SetError("Failed to query WASAPI client interface"); + } + + if (WASAPI_PrepDevice(_this, isrecovery) == -1) { + return -1; + } + + return 0; +} + +void +WASAPI_PlatformThreadInit(_THIS) +{ + // !!! FIXME: set this thread to "Pro Audio" priority. +} + +void +WASAPI_PlatformThreadDeinit(_THIS) +{ + // !!! FIXME: set this thread to "Pro Audio" priority. +} + +/* Everything below was copied from SDL_wasapi.c, before it got moved to SDL_immdevice.c! */ + +static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; +static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; + +extern "C" SDL_AudioFormat +WaveFormatToSDLFormat(WAVEFORMATEX *waveformat) +{ + if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) { + return AUDIO_F32SYS; + } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) { + return AUDIO_S16SYS; + } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) { + return AUDIO_S32SYS; + } else if (waveformat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + const WAVEFORMATEXTENSIBLE *ext = (const WAVEFORMATEXTENSIBLE *)waveformat; + if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) { + return AUDIO_F32SYS; + } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 16)) { + return AUDIO_S16SYS; + } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) { + return AUDIO_S32SYS; + } + } + return 0; +} + +static void +WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid) +{ + DevIdList *i; + DevIdList *next; + DevIdList *prev = NULL; + for (i = deviceid_list; i; i = next) { + next = i->next; + if (SDL_wcscmp(i->str, devid) == 0) { + if (prev) { + prev->next = next; + } + else { + deviceid_list = next; + } + SDL_RemoveAudioDevice(iscapture, i->str); + SDL_free(i->str); + SDL_free(i); + } else { + prev = i; + } + } +} + +static void +WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid) +{ + DevIdList *devidlist; + SDL_AudioSpec spec; + + /* You can have multiple endpoints on a device that are mutually exclusive ("Speakers" vs "Line Out" or whatever). + In a perfect world, things that are unplugged won't be in this collection. The only gotcha is probably for + phones and tablets, where you might have an internal speaker and a headphone jack and expect both to be + available and switch automatically. (!!! FIXME...?) */ + + /* see if we already have this one. */ + for (devidlist = deviceid_list; devidlist; devidlist = devidlist->next) { + if (SDL_wcscmp(devidlist->str, devid) == 0) { + return; /* we already have this. */ + } + } + + devidlist = (DevIdList *)SDL_malloc(sizeof(*devidlist)); + if (!devidlist) { + return; /* oh well. */ + } + + devid = SDL_wcsdup(devid); + if (!devid) { + SDL_free(devidlist); + return; /* oh well. */ + } + + devidlist->str = (WCHAR *)devid; + devidlist->next = deviceid_list; + deviceid_list = devidlist; + + SDL_zero(spec); + spec.channels = (Uint8)fmt->Format.nChannels; + spec.freq = fmt->Format.nSamplesPerSec; + spec.format = WaveFormatToSDLFormat((WAVEFORMATEX *)fmt); + SDL_AddAudioDevice(iscapture, devname, &spec, (void *)devid); +} + +#endif // SDL_AUDIO_DRIVER_WASAPI && defined(__WINRT__) + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/core/windows/SDL_hid.c b/src/core/windows/SDL_hid.c index bc679b453..d9dd04fbe 100644 --- a/src/core/windows/SDL_hid.c +++ b/src/core/windows/SDL_hid.c @@ -20,6 +20,8 @@ */ #include "../../SDL_internal.h" +#ifndef __WINRT__ + #include "SDL_hid.h" @@ -83,4 +85,6 @@ WIN_UnloadHIDDLL(void) } } +#endif /* !__WINRT__ */ + /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/core/windows/SDL_hid.h b/src/core/windows/SDL_hid.h index 70ec204de..82a001a2e 100644 --- a/src/core/windows/SDL_hid.h +++ b/src/core/windows/SDL_hid.h @@ -25,6 +25,8 @@ #include "SDL_windows.h" +#ifndef __WINRT__ + typedef LONG NTSTATUS; typedef USHORT USAGE; typedef struct _HIDP_PREPARSED_DATA *PHIDP_PREPARSED_DATA; @@ -195,6 +197,8 @@ extern HidP_GetValueCaps_t SDL_HidP_GetValueCaps; extern HidP_MaxDataListLength_t SDL_HidP_MaxDataListLength; extern HidP_GetData_t SDL_HidP_GetData; +#endif /* !__WINRT__ */ + #endif /* SDL_hid_h_ */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/core/windows/SDL_windows.c b/src/core/windows/SDL_windows.c index fc20d7eec..4ec9dbb64 100644 --- a/src/core/windows/SDL_windows.c +++ b/src/core/windows/SDL_windows.c @@ -20,7 +20,7 @@ */ #include "../../SDL_internal.h" -#if defined(__WIN32__) || defined(__GDK__) +#if defined(__WIN32__) || defined(__WINRT__) || defined(__GDK__) #include "SDL_windows.h" #include "SDL_error.h" @@ -89,7 +89,14 @@ WIN_CoInitialize(void) If you need multi-threaded mode, call CoInitializeEx() before SDL_Init() */ -#if defined(__XBOXONE__) || defined(__XBOXSERIES__) +#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; +#elif defined(__XBOXONE__) || defined(__XBOXSERIES__) /* On Xbox, there's no need to call CoInitializeEx (and it's not implemented) */ return S_OK; #else @@ -111,9 +118,12 @@ WIN_CoInitialize(void) void WIN_CoUninitialize(void) { +#ifndef __WINRT__ CoUninitialize(); +#endif } +#ifndef __WINRT__ void * WIN_LoadComBaseFunction(const char *name) { @@ -130,10 +140,14 @@ WIN_LoadComBaseFunction(const char *name) return NULL; } } +#endif HRESULT WIN_RoInitialize(void) { +#ifdef __WINRT__ + return S_OK; +#else typedef HRESULT (WINAPI *RoInitialize_t)(RO_INIT_TYPE initType); RoInitialize_t RoInitializeFunc = (RoInitialize_t)WIN_LoadComBaseFunction("RoInitialize"); if (RoInitializeFunc) { @@ -153,19 +167,22 @@ WIN_RoInitialize(void) } else { return E_NOINTERFACE; } +#endif } void WIN_RoUninitialize(void) { +#ifndef __WINRT__ typedef void (WINAPI *RoUninitialize_t)(void); RoUninitialize_t RoUninitializeFunc = (RoUninitialize_t)WIN_LoadComBaseFunction("RoUninitialize"); if (RoUninitializeFunc) { RoUninitializeFunc(); } +#endif } -#if !defined(__XBOXONE__) && !defined(__XBOXSERIES__) +#if !defined(__WINRT__) && !defined(__XBOXONE__) && !defined(__XBOXSERIES__) static BOOL IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) { @@ -189,7 +206,7 @@ IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServiceP BOOL WIN_IsWindowsVistaOrGreater(void) { -#if defined(__XBOXONE__) || defined(__XBOXSERIES__) +#if defined(__WINRT__) || defined(__XBOXONE__) || defined(__XBOXSERIES__) return TRUE; #else return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0); @@ -198,7 +215,7 @@ BOOL WIN_IsWindowsVistaOrGreater(void) BOOL WIN_IsWindows7OrGreater(void) { -#if defined(__XBOXONE__) || defined(__XBOXSERIES__) +#if defined(__WINRT__) || defined(__XBOXONE__) || defined(__XBOXSERIES__) return TRUE; #else return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 0); @@ -207,7 +224,7 @@ BOOL WIN_IsWindows7OrGreater(void) BOOL WIN_IsWindows8OrGreater(void) { -#if defined(__XBOXONE__) || defined(__XBOXSERIES__) +#if defined(__WINRT__) || defined(__XBOXONE__) || defined(__XBOXSERIES__) return TRUE; #else return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0); @@ -238,8 +255,8 @@ WASAPI doesn't need this. This is just for DirectSound/WinMM. char * WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid) { -#if defined(__XBOXONE__) || defined(__XBOXSERIES__) - return WIN_StringToUTF8(name); /* No registry access on Xbox, go with what we've got. */ +#if defined(__WINRT__) || defined(__XBOXONE__) || defined(__XBOXSERIES__) + return WIN_StringToUTF8(name); /* No registry access on WinRT/UWP and Xbox, go with what we've got. */ #else static const GUID nullguid = { 0 }; const unsigned char *ptr; @@ -291,7 +308,7 @@ WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid) retval = WIN_StringToUTF8(strw); SDL_free(strw); return retval ? retval : WIN_StringToUTF8(name); -#endif /**/ +#endif /* if __WINRT__ / else */ } BOOL @@ -324,6 +341,6 @@ WIN_RectToRECT(const SDL_Rect *sdlrect, RECT *winrect) winrect->bottom = sdlrect->y + sdlrect->h - 1; } -#endif /* defined(__WIN32__) || defined(__GDK__) */ +#endif /* defined(__WIN32__) || defined(__WINRT__) || defined(__GDK__) */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/core/windows/SDL_windows.h b/src/core/windows/SDL_windows.h index a47125e52..8b124d797 100644 --- a/src/core/windows/SDL_windows.h +++ b/src/core/windows/SDL_windows.h @@ -120,8 +120,10 @@ 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); +#if !defined(__WINRT__) /* Load a function from combase.dll */ void *WIN_LoadComBaseFunction(const char *name); +#endif /* Wrap up the oddities of CoInitialize() into a common function. */ extern HRESULT WIN_CoInitialize(void); diff --git a/src/core/windows/SDL_xinput.c b/src/core/windows/SDL_xinput.c index edf0d8bf3..8b9c26ef9 100644 --- a/src/core/windows/SDL_xinput.c +++ b/src/core/windows/SDL_xinput.c @@ -37,7 +37,7 @@ static HANDLE s_pXInputDLL = 0; static int s_XInputDLLRefCount = 0; -#if defined(__XBOXONE__) || defined(__XBOXSERIES__) +#if defined(__WINRT__) || defined(__XBOXONE__) || defined(__XBOXSERIES__) int WIN_LoadXInputDLL(void) @@ -70,7 +70,7 @@ WIN_UnloadXInputDLL(void) { } -#else /* !(defined(__XBOXONE__) || defined(__XBOXSERIES__)) */ +#else /* !(defined(__WINRT__) || defined(__XBOXONE__) || defined(__XBOXSERIES__)) */ int WIN_LoadXInputDLL(void) @@ -138,7 +138,7 @@ WIN_UnloadXInputDLL(void) } } -#endif /**/ +#endif /* __WINRT__ */ /* Ends C function definitions when using C++ */ #ifdef __cplusplus diff --git a/src/core/winrt/SDL_winrtapp_common.cpp b/src/core/winrt/SDL_winrtapp_common.cpp new file mode 100644 index 000000000..6e98a78d9 --- /dev/null +++ b/src/core/winrt/SDL_winrtapp_common.cpp @@ -0,0 +1,67 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_internal.h" + +#include "SDL_main.h" +#include "SDL_system.h" +#include "SDL_winrtapp_direct3d.h" +#include "SDL_winrtapp_xaml.h" + +#include + +int (*WINRT_SDLAppEntryPoint)(int, char **) = NULL; + +extern "C" DECLSPEC int +SDL_WinRTRunApp(SDL_main_func mainFunction, void * xamlBackgroundPanel) +{ + if (xamlBackgroundPanel) { + return SDL_WinRTInitXAMLApp(mainFunction, xamlBackgroundPanel); + } else { + if (FAILED(Windows::Foundation::Initialize(RO_INIT_MULTITHREADED))) { + return 1; + } + return SDL_WinRTInitNonXAMLApp(mainFunction); + } +} + + +extern "C" DECLSPEC SDL_WinRT_DeviceFamily +SDL_WinRTGetDeviceFamily() +{ +#if NTDDI_VERSION >= NTDDI_WIN10 /* !!! FIXME: I have no idea if this is the right test. This is a UWP API, I think. Older windows should...just return "mobile"? I don't know. --ryan. */ + Platform::String^ deviceFamily = Windows::System::Profile::AnalyticsInfo::VersionInfo->DeviceFamily; + + if (deviceFamily->Equals("Windows.Desktop")) + { + return SDL_WINRT_DEVICEFAMILY_DESKTOP; + } + else if (deviceFamily->Equals("Windows.Mobile")) + { + return SDL_WINRT_DEVICEFAMILY_MOBILE; + } + else if (deviceFamily->Equals("Windows.Xbox")) + { + return SDL_WINRT_DEVICEFAMILY_XBOX; + } +#endif + + return SDL_WINRT_DEVICEFAMILY_UNKNOWN; +} diff --git a/src/core/winrt/SDL_winrtapp_common.h b/src/core/winrt/SDL_winrtapp_common.h new file mode 100644 index 000000000..79b100276 --- /dev/null +++ b/src/core/winrt/SDL_winrtapp_common.h @@ -0,0 +1,31 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_winrtapp_common_h_ +#define SDL_winrtapp_common_h_ + +/* A pointer to the app's C-style main() function (which is a different + function than the WinRT app's actual entry point). + */ +extern int (*WINRT_SDLAppEntryPoint)(int, char **); + +#endif // SDL_winrtapp_common_h_ diff --git a/src/core/winrt/SDL_winrtapp_direct3d.cpp b/src/core/winrt/SDL_winrtapp_direct3d.cpp new file mode 100644 index 000000000..1f0962fca --- /dev/null +++ b/src/core/winrt/SDL_winrtapp_direct3d.cpp @@ -0,0 +1,797 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_internal.h" + +/* Standard C++11 includes */ +#include +#include +#include +using namespace std; + + +/* Windows includes */ +#include "ppltasks.h" +using namespace concurrency; +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; + +#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP +using namespace Windows::Phone::UI::Input; +#endif + + +/* SDL includes */ +extern "C" { +#include "SDL_events.h" +#include "SDL_hints.h" +#include "SDL_main.h" +#include "SDL_stdinc.h" +#include "SDL_render.h" +#include "../../video/SDL_sysvideo.h" +//#include "../../SDL_hints_c.h" +#include "../../events/SDL_events_c.h" +#include "../../events/SDL_keyboard_c.h" +#include "../../events/SDL_mouse_c.h" +#include "../../events/SDL_windowevents_c.h" +#include "../../render/SDL_sysrender.h" +#include "../windows/SDL_windows.h" +} + +#include "../../video/winrt/SDL_winrtevents_c.h" +#include "../../video/winrt/SDL_winrtvideo_cpp.h" +#include "SDL_winrtapp_common.h" +#include "SDL_winrtapp_direct3d.h" + +#if SDL_VIDEO_RENDER_D3D11 && !SDL_RENDER_DISABLED +/* Calling IDXGIDevice3::Trim on the active Direct3D 11.x device is necessary + * when Windows 8.1 apps are about to get suspended. + */ +extern "C" void D3D11_Trim(SDL_Renderer *); +#endif + + +// 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: record a reference to the global, WinRT '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; +} + +int SDL_WinRTInitNonXAMLApp(int (*mainFunction)(int, char **)) +{ + WINRT_SDLAppEntryPoint = mainFunction; + auto direct3DApplicationSource = ref new SDLApplicationSource(); + CoreApplication::Run(direct3DApplicationSource); + return 0; +} + +static void +WINRT_ProcessWindowSizeChange() // TODO: Pass an SDL_Window-identifying thing into WINRT_ProcessWindowSizeChange() +{ + CoreWindow ^ coreWindow = CoreWindow::GetForCurrentThread(); + if (coreWindow) { + if (WINRT_GlobalSDLWindow) { + SDL_Window * window = WINRT_GlobalSDLWindow; + SDL_WindowData * data = (SDL_WindowData *) window->driverdata; + + int x = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Left); + int y = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Top); + int w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width); + int h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height); + +#if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) && (NTDDI_VERSION == NTDDI_WIN8) + /* WinPhone 8.0 always keeps its native window size in portrait, + regardless of orientation. This changes in WinPhone 8.1, + in which the native window's size changes along with + orientation. + + Attempt to emulate WinPhone 8.1's behavior on WinPhone 8.0, with + regards to window size. This fixes a rendering bug that occurs + when a WinPhone 8.0 app is rotated to either 90 or 270 degrees. + */ + const DisplayOrientations currentOrientation = WINRT_DISPLAY_PROPERTY(CurrentOrientation); + switch (currentOrientation) { + case DisplayOrientations::Landscape: + case DisplayOrientations::LandscapeFlipped: { + int tmp = w; + w = h; + h = tmp; + } break; + } +#endif + + const Uint32 latestFlags = WINRT_DetectWindowFlags(window); + if (latestFlags & SDL_WINDOW_MAXIMIZED) { + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MAXIMIZED, 0, 0); + } else { + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0); + } + + WINRT_UpdateWindowFlags(window, SDL_WINDOW_FULLSCREEN_DESKTOP); + + /* The window can move during a resize event, such as when maximizing + or resizing from a corner */ + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y); + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, w, h); + } + } +} + +SDL_WinRTApp::SDL_WinRTApp() : + m_windowClosed(false), + m_windowVisible(true) +{ +} + +void SDL_WinRTApp::Initialize(CoreApplicationView^ applicationView) +{ + applicationView->Activated += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnAppActivated); + + CoreApplication::Suspending += + ref new EventHandler(this, &SDL_WinRTApp::OnSuspending); + + CoreApplication::Resuming += + ref new EventHandler(this, &SDL_WinRTApp::OnResuming); + + CoreApplication::Exiting += + ref new EventHandler(this, &SDL_WinRTApp::OnExiting); + +#if NTDDI_VERSION >= NTDDI_WIN10 + /* HACK ALERT! Xbox One doesn't seem to detect gamepads unless something + gets registered to receive Win10's Windows.Gaming.Input.Gamepad.GamepadAdded + events. We'll register an event handler for these events here, to make + sure that gamepad detection works later on, if requested. + */ + Windows::Gaming::Input::Gamepad::GamepadAdded += + ref new Windows::Foundation::EventHandler( + this, &SDL_WinRTApp::OnGamepadAdded + ); +#endif +} + +#if NTDDI_VERSION > NTDDI_WIN8 +void SDL_WinRTApp::OnOrientationChanged(DisplayInformation^ sender, Object^ args) +#else +void SDL_WinRTApp::OnOrientationChanged(Object^ sender) +#endif +{ +#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 Bounds={%f,%f,%f,%f}\n", + __FUNCTION__, + WINRT_DISPLAY_PROPERTY(CurrentOrientation), + WINRT_DISPLAY_PROPERTY(NativeOrientation), + WINRT_DISPLAY_PROPERTY(AutoRotationPreferences), + window->Bounds.X, + window->Bounds.Y, + window->Bounds.Width, + window->Bounds.Height); + } else { + SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d\n", + __FUNCTION__, + WINRT_DISPLAY_PROPERTY(CurrentOrientation), + WINRT_DISPLAY_PROPERTY(NativeOrientation), + WINRT_DISPLAY_PROPERTY(AutoRotationPreferences)); + } + } +#endif + + WINRT_ProcessWindowSizeChange(); + +#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP + // HACK: Make sure that orientation changes + // lead to the Direct3D renderer's viewport getting updated: + // + // For some reason, this doesn't seem to need to be done on Windows 8.x, + // even when going from Landscape to LandscapeFlipped. It only seems to + // be needed on Windows Phone, at least when I tested on my devices. + // I'm not currently sure why this is, but it seems to work fine. -- David L. + // + // TODO, WinRT: do more extensive research into why orientation changes on Win 8.x don't need D3D changes, or if they might, in some cases + SDL_Window * window = WINRT_GlobalSDLWindow; + if (window) { + SDL_WindowData * data = (SDL_WindowData *)window->driverdata; + int w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width); + int h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height); + SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_SIZE_CHANGED, w, h); + } +#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 bounds={%f, %f, %f,%f}\n", + __FUNCTION__, + WINRT_DISPLAY_PROPERTY(CurrentOrientation), + WINRT_DISPLAY_PROPERTY(NativeOrientation), + WINRT_DISPLAY_PROPERTY(AutoRotationPreferences), + window->Bounds.X, + window->Bounds.Y, + 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->Activated += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnWindowActivated); + + 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->PointerMoved += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnPointerMoved); + + window->PointerReleased += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnPointerReleased); + + window->PointerEntered += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnPointerEntered); + + window->PointerExited += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnPointerExited); + + window->PointerWheelChanged += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnPointerWheelChanged); + +#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); + + window->CharacterReceived += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnCharacterReceived); + +#if NTDDI_VERSION >= NTDDI_WIN10 + Windows::UI::Core::SystemNavigationManager::GetForCurrentView()->BackRequested += + ref new EventHandler(this, &SDL_WinRTApp::OnBackButtonPressed); +#elif WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP + HardwareButtons::BackPressed += + ref new EventHandler(this, &SDL_WinRTApp::OnBackButtonPressed); +#endif + +#if NTDDI_VERSION > NTDDI_WIN8 + DisplayInformation::GetForCurrentView()->OrientationChanged += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnOrientationChanged); +#else + DisplayProperties::OrientationChanged += + ref new DisplayPropertiesEventHandler(this, &SDL_WinRTApp::OnOrientationChanged); +#endif + +#if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10) // for Windows 8/8.1/RT apps... (and not Phone apps) + // Make sure we know when a user has opened the app's settings pane. + // This is needed in order to display a privacy policy, which needs + // to be done for network-enabled apps, as per Windows Store requirements. + using namespace Windows::UI::ApplicationSettings; + SettingsPane::GetForCurrentView()->CommandsRequested += + ref new TypedEventHandler + (this, &SDL_WinRTApp::OnSettingsPaneCommandsRequested); +#endif +} + +void SDL_WinRTApp::Load(Platform::String^ entryPoint) +{ +} + +void SDL_WinRTApp::Run() +{ + SDL_SetMainReady(); + if (WINRT_SDLAppEntryPoint) + { + // TODO, WinRT: pass the C-style main() a reasonably realistic + // representation of command line arguments. + int argc = 1; + char **argv = (char **)SDL_malloc(2 * sizeof(*argv)); + if (!argv) { + return; + } + argv[0] = SDL_strdup("WinRTApp"); + argv[1] = NULL; + WINRT_SDLAppEntryPoint(argc, argv); + SDL_free(argv[0]); + SDL_free(argv); + } +} + +static bool IsSDLWindowEventPending(SDL_WindowEventID windowEventID) +{ + SDL_Event events[128]; + const int count = SDL_PeepEvents(events, sizeof(events)/sizeof(SDL_Event), SDL_PEEKEVENT, SDL_WINDOWEVENT, SDL_WINDOWEVENT); + for (int i = 0; i < count; ++i) { + if (events[i].window.event == windowEventID) { + return true; + } + } + return false; +} + +bool SDL_WinRTApp::ShouldWaitForAppResumeEvents() +{ + /* Don't wait if the app is visible: */ + if (m_windowVisible) { + return false; + } + + /* Don't wait until the window-hide events finish processing. + * Do note that if an app-suspend event is sent (as indicated + * by SDL_APP_WILLENTERBACKGROUND and SDL_APP_DIDENTERBACKGROUND + * events), then this code may be a moot point, as WinRT's + * own event pump (aka ProcessEvents()) will pause regardless + * of what we do here. This happens on Windows Phone 8, to note. + * Windows 8.x apps, on the other hand, may get a chance to run + * these. + */ + if (IsSDLWindowEventPending(SDL_WINDOWEVENT_HIDDEN)) { + return false; + } else if (IsSDLWindowEventPending(SDL_WINDOWEVENT_FOCUS_LOST)) { + return false; + } else if (IsSDLWindowEventPending(SDL_WINDOWEVENT_MINIMIZED)) { + return false; + } + + return true; +} + +void SDL_WinRTApp::PumpEvents() +{ + if (!m_windowClosed) { + if (!ShouldWaitForAppResumeEvents()) { + /* This is the normal way in which events should be pumped. + * 'ProcessAllIfPresent' will make ProcessEvents() process anywhere + * from zero to N events, and will then return. + */ + CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent); + } else { + /* This style of event-pumping, with 'ProcessOneAndAllPending', + * will cause anywhere from one to N events to be processed. If + * at least one event is processed, the call will return. If + * no events are pending, then the call will wait until one is + * available, and will not return (to the caller) until this + * happens! This should only occur when the app is hidden. + */ + CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending); + } + } +} + +void SDL_WinRTApp::Uninitialize() +{ +} + +#if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10) +void SDL_WinRTApp::OnSettingsPaneCommandsRequested( + Windows::UI::ApplicationSettings::SettingsPane ^p, + Windows::UI::ApplicationSettings::SettingsPaneCommandsRequestedEventArgs ^args) +{ + using namespace Platform; + using namespace Windows::UI::ApplicationSettings; + using namespace Windows::UI::Popups; + + String ^privacyPolicyURL = nullptr; // a URL to an app's Privacy Policy + String ^privacyPolicyLabel = nullptr; // label/link text + const char *tmpHintValue = NULL; // SDL_GetHint-retrieved value, used immediately + wchar_t *tmpStr = NULL; // used for UTF8 to UCS2 conversion + + // Setup a 'Privacy Policy' link, if one is available (via SDL_GetHint): + tmpHintValue = SDL_GetHint(SDL_HINT_WINRT_PRIVACY_POLICY_URL); + if (tmpHintValue && tmpHintValue[0] != '\0') { + // Convert the privacy policy's URL to UCS2: + tmpStr = WIN_UTF8ToString(tmpHintValue); + privacyPolicyURL = ref new String(tmpStr); + SDL_free(tmpStr); + + // Optionally retrieve custom label-text for the link. If this isn't + // available, a default value will be used instead. + tmpHintValue = SDL_GetHint(SDL_HINT_WINRT_PRIVACY_POLICY_LABEL); + if (tmpHintValue && tmpHintValue[0] != '\0') { + tmpStr = WIN_UTF8ToString(tmpHintValue); + privacyPolicyLabel = ref new String(tmpStr); + SDL_free(tmpStr); + } else { + privacyPolicyLabel = ref new String(L"Privacy Policy"); + } + + // Register the link, along with a handler to be called if and when it is + // clicked: + auto cmd = ref new SettingsCommand(L"privacyPolicy", privacyPolicyLabel, + ref new UICommandInvokedHandler([=](IUICommand ^) { + Windows::System::Launcher::LaunchUriAsync(ref new Uri(privacyPolicyURL)); + })); + args->Request->ApplicationCommands->Append(cmd); + } +} +#endif // if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10) + +void SDL_WinRTApp::OnWindowSizeChanged(CoreWindow^ sender, WindowSizeChangedEventArgs^ args) +{ +#if LOG_WINDOW_EVENTS==1 + SDL_Log("%s, size={%f,%f}, bounds={%f,%f,%f,%f}, current orientation=%d, native orientation=%d, auto rot. pref=%d, WINRT_GlobalSDLWindow?=%s\n", + __FUNCTION__, + args->Size.Width, args->Size.Height, + sender->Bounds.X, sender->Bounds.Y, sender->Bounds.Width, sender->Bounds.Height, + WINRT_DISPLAY_PROPERTY(CurrentOrientation), + WINRT_DISPLAY_PROPERTY(NativeOrientation), + WINRT_DISPLAY_PROPERTY(AutoRotationPreferences), + (WINRT_GlobalSDLWindow ? "yes" : "no")); +#endif + + WINRT_ProcessWindowSizeChange(); +} + +void SDL_WinRTApp::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEventArgs^ args) +{ +#if LOG_WINDOW_EVENTS==1 + SDL_Log("%s, visible?=%s, bounds={%f,%f,%f,%f}, WINRT_GlobalSDLWindow?=%s\n", + __FUNCTION__, + (args->Visible ? "yes" : "no"), + sender->Bounds.X, sender->Bounds.Y, + sender->Bounds.Width, sender->Bounds.Height, + (WINRT_GlobalSDLWindow ? "yes" : "no")); +#endif + + m_windowVisible = args->Visible; + if (WINRT_GlobalSDLWindow) { + SDL_bool wasSDLWindowSurfaceValid = WINRT_GlobalSDLWindow->surface_valid; + Uint32 latestWindowFlags = WINRT_DetectWindowFlags(WINRT_GlobalSDLWindow); + if (args->Visible) { + SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_SHOWN, 0, 0); + SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0); + if (latestWindowFlags & SDL_WINDOW_MAXIMIZED) { + SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_MAXIMIZED, 0, 0); + } else { + SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_RESTORED, 0, 0); + } + } else { + SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_HIDDEN, 0, 0); + SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0); + SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_WINDOWEVENT_MINIMIZED, 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. + WINRT_GlobalSDLWindow->surface_valid = wasSDLWindowSurfaceValid; + } +} + +void SDL_WinRTApp::OnWindowActivated(CoreWindow^ sender, WindowActivatedEventArgs^ args) +{ +#if LOG_WINDOW_EVENTS==1 + SDL_Log("%s, WINRT_GlobalSDLWindow?=%s\n\n", + __FUNCTION__, + (WINRT_GlobalSDLWindow ? "yes" : "no")); +#endif + + /* There's no property in Win 8.x to tell whether a window is active or + not. [De]activation events are, however, sent to the app. We'll just + record those, in case the CoreWindow gets wrapped by an SDL_Window at + some future time. + */ + sender->CustomProperties->Insert("SDLHelperWindowActivationState", args->WindowActivationState); + + SDL_Window * window = WINRT_GlobalSDLWindow; + if (window) { + if (args->WindowActivationState != CoreWindowActivationState::Deactivated) { + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_SHOWN, 0, 0); + if (SDL_GetKeyboardFocus() != window) { + SDL_SetKeyboardFocus(window); + } + + /* Send a mouse-motion event as appropriate. + This doesn't work when called from OnPointerEntered, at least + not in WinRT CoreWindow apps (as OnPointerEntered doesn't + appear to be called after window-reactivation, at least not + in Windows 10, Build 10586.3 (November 2015 update, non-beta). + + Don't do it on WinPhone 8.0 though, as CoreWindow's 'PointerPosition' + property isn't available. + */ +#if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP) || (NTDDI_VERSION >= NTDDI_WINBLUE) + Point cursorPos = WINRT_TransformCursorPosition(window, sender->PointerPosition, TransformToSDLWindowSize); + SDL_SendMouseMotion(window, 0, 0, (int)cursorPos.X, (int)cursorPos.Y); +#endif + + /* TODO, WinRT: see if the Win32 bugfix from https://hg.libsdl.org/SDL/rev/d278747da408 needs to be applied (on window activation) */ + //WIN_CheckAsyncMouseRelease(data); + + /* TODO, WinRT: implement clipboard support, if possible */ + ///* + // * FIXME: Update keyboard state + // */ + //WIN_CheckClipboardUpdate(data->videodata); + + // HACK: Resetting the mouse-cursor here seems to fix + // https://bugzilla.libsdl.org/show_bug.cgi?id=3217, whereby a + // WinRT app's mouse cursor may switch to Windows' 'wait' cursor, + // after a user alt-tabs back into a full-screened SDL app. + // This bug does not appear to reproduce 100% of the time. + // It may be a bug in Windows itself (v.10.0.586.36, as tested, + // and the most-recent as of this writing). + SDL_SetCursor(NULL); + } else { + if (SDL_GetKeyboardFocus() == window) { + SDL_SetKeyboardFocus(NULL); + } + } + } +} + +void SDL_WinRTApp::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args) +{ +#if LOG_WINDOW_EVENTS==1 + SDL_Log("%s\n", __FUNCTION__); +#endif + m_windowClosed = true; +} + +void SDL_WinRTApp::OnAppActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args) +{ + CoreWindow::GetForCurrentThread()->Activate(); +} + +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. + + // ... but first, let the app know it's about to go to the background. + // The separation of events may be important, given that the deferral + // runs in a separate thread. This'll make SDL_APP_WILLENTERBACKGROUND + // the only event among the two that runs in the main thread. Given + // that a few WinRT operations can only be done from the main thread + // (things that access the WinRT CoreWindow are one example of this), + // this could be important. + SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND); + + SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral(); + create_task([this, deferral]() + { + // Send an app did-enter-background 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. + SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND); + + // Let the Direct3D 11 renderer prepare for the app to be backgrounded. + // This is necessary for Windows 8.1, possibly elsewhere in the future. + // More details at: http://msdn.microsoft.com/en-us/library/windows/apps/Hh994929.aspx +#if SDL_VIDEO_RENDER_D3D11 && !SDL_RENDER_DISABLED + if (WINRT_GlobalSDLWindow) { + SDL_Renderer * renderer = SDL_GetRenderer(WINRT_GlobalSDLWindow); + if (renderer && (SDL_strcmp(renderer->info.name, "direct3d11") == 0)) { + D3D11_Trim(renderer); + } + } +#endif + + 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 these events + // do not occur if the app was previously terminated. + SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND); + SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND); +} + +void SDL_WinRTApp::OnExiting(Platform::Object^ sender, Platform::Object^ args) +{ + SDL_SendAppEvent(SDL_APP_TERMINATING); +} + +static void +WINRT_LogPointerEvent(const char * header, Windows::UI::Core::PointerEventArgs ^ args, Windows::Foundation::Point transformedPoint) +{ + Uint8 button, pressed; + Windows::UI::Input::PointerPoint ^ pt = args->CurrentPoint; + WINRT_GetSDLButtonForPointerPoint(pt, &button, &pressed); + SDL_Log("%s: Position={%f,%f}, Transformed Pos={%f, %f}, MouseWheelDelta=%d, FrameId=%d, PointerId=%d, SDL button=%d pressed=%d\n", + header, + pt->Position.X, pt->Position.Y, + transformedPoint.X, transformedPoint.Y, + pt->Properties->MouseWheelDelta, + pt->FrameId, + pt->PointerId, + button, + pressed); +} + +void SDL_WinRTApp::OnPointerPressed(CoreWindow^ sender, PointerEventArgs^ args) +{ +#if LOG_POINTER_EVENTS + WINRT_LogPointerEvent("pointer pressed", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize)); +#endif + + WINRT_ProcessPointerPressedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint); +} + +void SDL_WinRTApp::OnPointerMoved(CoreWindow^ sender, PointerEventArgs^ args) +{ +#if LOG_POINTER_EVENTS + WINRT_LogPointerEvent("pointer moved", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize)); +#endif + + WINRT_ProcessPointerMovedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint); +} + +void SDL_WinRTApp::OnPointerReleased(CoreWindow^ sender, PointerEventArgs^ args) +{ +#if LOG_POINTER_EVENTS + WINRT_LogPointerEvent("pointer released", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize)); +#endif + + WINRT_ProcessPointerReleasedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint); +} + +void SDL_WinRTApp::OnPointerEntered(CoreWindow^ sender, PointerEventArgs^ args) +{ +#if LOG_POINTER_EVENTS + WINRT_LogPointerEvent("pointer entered", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize)); +#endif + + WINRT_ProcessPointerEnteredEvent(WINRT_GlobalSDLWindow, args->CurrentPoint); +} + +void SDL_WinRTApp::OnPointerExited(CoreWindow^ sender, PointerEventArgs^ args) +{ +#if LOG_POINTER_EVENTS + WINRT_LogPointerEvent("pointer exited", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize)); +#endif + + WINRT_ProcessPointerExitedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint); +} + +void SDL_WinRTApp::OnPointerWheelChanged(CoreWindow^ sender, PointerEventArgs^ args) +{ +#if LOG_POINTER_EVENTS + WINRT_LogPointerEvent("pointer wheel changed", args, WINRT_TransformCursorPosition(WINRT_GlobalSDLWindow, args->CurrentPoint->Position, TransformToSDLWindowSize)); +#endif + + WINRT_ProcessPointerWheelChangedEvent(WINRT_GlobalSDLWindow, args->CurrentPoint); +} + +void SDL_WinRTApp::OnMouseMoved(MouseDevice^ mouseDevice, MouseEventArgs^ args) +{ + WINRT_ProcessMouseMovedEvent(WINRT_GlobalSDLWindow, args); +} + +void SDL_WinRTApp::OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args) +{ + WINRT_ProcessKeyDownEvent(args); +} + +void SDL_WinRTApp::OnKeyUp(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args) +{ + WINRT_ProcessKeyUpEvent(args); +} + +void SDL_WinRTApp::OnCharacterReceived(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CharacterReceivedEventArgs^ args) +{ + WINRT_ProcessCharacterReceivedEvent(args); +} + +template +static void WINRT_OnBackButtonPressed(BackButtonEventArgs ^ args) +{ + SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_AC_BACK); + SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_AC_BACK); + + if (SDL_GetHintBoolean(SDL_HINT_WINRT_HANDLE_BACK_BUTTON, SDL_FALSE)) { + args->Handled = true; + } +} + +#if NTDDI_VERSION >= NTDDI_WIN10 +void SDL_WinRTApp::OnBackButtonPressed(Platform::Object^ sender, Windows::UI::Core::BackRequestedEventArgs^ args) + +{ + WINRT_OnBackButtonPressed(args); +} +#elif WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP +void SDL_WinRTApp::OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args) + +{ + WINRT_OnBackButtonPressed(args); +} +#endif + +#if NTDDI_VERSION >= NTDDI_WIN10 +void SDL_WinRTApp::OnGamepadAdded(Platform::Object ^sender, Windows::Gaming::Input::Gamepad ^gamepad) +{ + /* HACK ALERT: Nothing needs to be done here, as this method currently + only exists to allow something to be registered with Win10's + GamepadAdded event, an operation that seems to be necessary to get + Xinput-based detection to work on Xbox One. + */ +} +#endif + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/core/winrt/SDL_winrtapp_direct3d.h b/src/core/winrt/SDL_winrtapp_direct3d.h new file mode 100644 index 000000000..1f7c917a6 --- /dev/null +++ b/src/core/winrt/SDL_winrtapp_direct3d.h @@ -0,0 +1,92 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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 + +extern int SDL_WinRTInitNonXAMLApp(int (*mainFunction)(int, char **)); + +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 + void PumpEvents(); + +protected: + bool ShouldWaitForAppResumeEvents(); + + // Event Handlers. + +#if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10) // for Windows 8/8.1/RT apps... (and not Phone apps) + void OnSettingsPaneCommandsRequested( + Windows::UI::ApplicationSettings::SettingsPane ^p, + Windows::UI::ApplicationSettings::SettingsPaneCommandsRequestedEventArgs ^args); +#endif // if (WINAPI_FAMILY == WINAPI_FAMILY_APP) && (NTDDI_VERSION < NTDDI_WIN10) + +#if NTDDI_VERSION > NTDDI_WIN8 + void OnOrientationChanged(Windows::Graphics::Display::DisplayInformation^ sender, Platform::Object^ args); +#else + void OnOrientationChanged(Platform::Object^ sender); +#endif + void OnWindowSizeChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::WindowSizeChangedEventArgs^ args); + void OnLogicalDpiChanged(Platform::Object^ sender); + void OnAppActivated(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 OnExiting(Platform::Object^ sender, Platform::Object^ args); + void OnWindowActivated(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::WindowActivatedEventArgs^ 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 OnPointerEntered(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); + void OnPointerExited(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); + void OnCharacterReceived(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CharacterReceivedEventArgs^ args); + +#if NTDDI_VERSION >= NTDDI_WIN10 + void OnBackButtonPressed(Platform::Object^ sender, Windows::UI::Core::BackRequestedEventArgs^ args); +#elif WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP + void OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args); +#endif + +#if NTDDI_VERSION >= NTDDI_WIN10 + void OnGamepadAdded(Platform::Object ^sender, Windows::Gaming::Input::Gamepad ^gamepad); +#endif + +private: + bool m_windowClosed; + bool m_windowVisible; +}; + +extern SDL_WinRTApp ^ SDL_WinRTGlobalApp; diff --git a/src/core/winrt/SDL_winrtapp_xaml.cpp b/src/core/winrt/SDL_winrtapp_xaml.cpp new file mode 100644 index 000000000..69716e11d --- /dev/null +++ b/src/core/winrt/SDL_winrtapp_xaml.cpp @@ -0,0 +1,160 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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. +*/ + +/* Windows includes */ +#include +#include + +#if WINAPI_FAMILY == WINAPI_FAMILY_APP +#include +#endif + + +/* SDL includes */ +#include "../../SDL_internal.h" +#include "SDL.h" +#include "../../video/winrt/SDL_winrtevents_c.h" +#include "../../video/winrt/SDL_winrtvideo_cpp.h" +#include "SDL_winrtapp_common.h" +#include "SDL_winrtapp_xaml.h" + + + +/* SDL-internal globals: */ +SDL_bool WINRT_XAMLWasEnabled = SDL_FALSE; + +#if WINAPI_FAMILY == WINAPI_FAMILY_APP +extern "C" +ISwapChainBackgroundPanelNative * WINRT_GlobalSwapChainBackgroundPanelNative = NULL; +static Windows::Foundation::EventRegistrationToken WINRT_XAMLAppEventToken; +#endif + + +/* + * Input event handlers (XAML) + */ +#if WINAPI_FAMILY == WINAPI_FAMILY_APP + +static void +WINRT_OnPointerPressedViaXAML(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ args) +{ + WINRT_ProcessPointerPressedEvent(WINRT_GlobalSDLWindow, args->GetCurrentPoint(nullptr)); +} + +static void +WINRT_OnPointerMovedViaXAML(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ args) +{ + WINRT_ProcessPointerMovedEvent(WINRT_GlobalSDLWindow, args->GetCurrentPoint(nullptr)); +} + +static void +WINRT_OnPointerReleasedViaXAML(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ args) +{ + WINRT_ProcessPointerReleasedEvent(WINRT_GlobalSDLWindow, args->GetCurrentPoint(nullptr)); +} + +static void +WINRT_OnPointerWheelChangedViaXAML(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ args) +{ + WINRT_ProcessPointerWheelChangedEvent(WINRT_GlobalSDLWindow, args->GetCurrentPoint(nullptr)); +} + +#endif // WINAPI_FAMILY == WINAPI_FAMILY_APP + + +/* + * XAML-to-SDL Rendering Callback + */ +#if WINAPI_FAMILY == WINAPI_FAMILY_APP + +static void +WINRT_OnRenderViaXAML(_In_ Platform::Object^ sender, _In_ Platform::Object^ args) +{ + WINRT_CycleXAMLThread(); +} + +#endif // WINAPI_FAMILY == WINAPI_FAMILY_APP + + +/* + * SDL + XAML Initialization + */ + +int +SDL_WinRTInitXAMLApp(int (*mainFunction)(int, char **), void * backgroundPanelAsIInspectable) +{ +#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP + return SDL_SetError("XAML support is not yet available in Windows Phone."); +#else + // Declare C++/CX namespaces: + using namespace Platform; + using namespace Windows::Foundation; + using namespace Windows::UI::Core; + using namespace Windows::UI::Xaml; + using namespace Windows::UI::Xaml::Controls; + using namespace Windows::UI::Xaml::Input; + using namespace Windows::UI::Xaml::Media; + + // Make sure we have a valid XAML element (to draw onto): + if ( ! backgroundPanelAsIInspectable) { + return SDL_InvalidParamError("backgroundPanelAsIInspectable"); + } + + Platform::Object ^ backgroundPanel = reinterpret_cast((IInspectable *) backgroundPanelAsIInspectable); + SwapChainBackgroundPanel ^swapChainBackgroundPanel = dynamic_cast(backgroundPanel); + if ( ! swapChainBackgroundPanel) { + return SDL_SetError("An unknown or unsupported type of XAML control was specified."); + } + + // Setup event handlers: + swapChainBackgroundPanel->PointerPressed += ref new PointerEventHandler(WINRT_OnPointerPressedViaXAML); + swapChainBackgroundPanel->PointerReleased += ref new PointerEventHandler(WINRT_OnPointerReleasedViaXAML); + swapChainBackgroundPanel->PointerWheelChanged += ref new PointerEventHandler(WINRT_OnPointerWheelChangedViaXAML); + swapChainBackgroundPanel->PointerMoved += ref new PointerEventHandler(WINRT_OnPointerMovedViaXAML); + + // Setup for rendering: + IInspectable *panelInspectable = (IInspectable*) reinterpret_cast(swapChainBackgroundPanel); + panelInspectable->QueryInterface(__uuidof(ISwapChainBackgroundPanelNative), (void **)&WINRT_GlobalSwapChainBackgroundPanelNative); + + WINRT_XAMLAppEventToken = CompositionTarget::Rendering::add(ref new EventHandler(WINRT_OnRenderViaXAML)); + + // Make sure the app is ready to call the SDL-centric main() function: + WINRT_SDLAppEntryPoint = mainFunction; + SDL_SetMainReady(); + + // Make sure video-init knows that we're initializing XAML: + SDL_bool oldXAMLWasEnabledValue = WINRT_XAMLWasEnabled; + WINRT_XAMLWasEnabled = SDL_TRUE; + + // Make sure video modes are detected now, while we still have access to the WinRT + // CoreWindow. WinRT will not allow the app's CoreWindow to be accessed via the + // SDL/WinRT thread. + if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { + // SDL_InitSubSystem will, on error, set the SDL error. Let that propogate to + // the caller to here: + WINRT_XAMLWasEnabled = oldXAMLWasEnabledValue; + return -1; + } + + // All done, for now. + return 0; +#endif // WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP / else +} diff --git a/src/core/winrt/SDL_winrtapp_xaml.h b/src/core/winrt/SDL_winrtapp_xaml.h new file mode 100644 index 000000000..e26e21884 --- /dev/null +++ b/src/core/winrt/SDL_winrtapp_xaml.h @@ -0,0 +1,33 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_winrtapp_xaml_h_ +#define SDL_winrtapp_xaml_h_ + +#include "SDL_stdinc.h" + +#ifdef __cplusplus +extern SDL_bool WINRT_XAMLWasEnabled; +extern int SDL_WinRTInitXAMLApp(int (*mainFunction)(int, char **), void * backgroundPanelAsIInspectable); +#endif // ifdef __cplusplus + +#endif // SDL_winrtapp_xaml_h_ diff --git a/src/cpuinfo/SDL_cpuinfo.c b/src/cpuinfo/SDL_cpuinfo.c index 2b3324e84..24c188da6 100644 --- a/src/cpuinfo/SDL_cpuinfo.c +++ b/src/cpuinfo/SDL_cpuinfo.c @@ -24,7 +24,7 @@ #include "../SDL_internal.h" #endif -#if defined(__WIN32__) || defined(__GDK__) +#if defined(__WIN32__) || defined(__WINRT__) || defined(__GDK__) #include "../core/windows/SDL_windows.h" #endif @@ -444,7 +444,7 @@ CPU_haveNEON(void) query the OS kernel in a platform-specific way. :/ */ #if defined(SDL_CPUINFO_DISABLED) return 0; /* disabled */ -#elif (defined(__WINDOWS__) || defined(__GDK__)) && (defined(_M_ARM) || defined(_M_ARM64)) +#elif (defined(__WINDOWS__) || defined(__WINRT__) || defined(__GDK__)) && (defined(_M_ARM) || defined(_M_ARM64)) /* Visual Studio, for ARM, doesn't define __ARM_ARCH. Handle this first. */ /* Seems to have been removed */ # if !defined(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE) diff --git a/src/dynapi/SDL_dynapi.h b/src/dynapi/SDL_dynapi.h index cbc300e75..1182b637e 100644 --- a/src/dynapi/SDL_dynapi.h +++ b/src/dynapi/SDL_dynapi.h @@ -49,6 +49,8 @@ #define SDL_DYNAMIC_API 0 #elif defined(__EMSCRIPTEN__) && __EMSCRIPTEN__ /* probably not useful on Emscripten. */ #define SDL_DYNAMIC_API 0 +#elif defined(SDL_BUILDING_WINRT) && SDL_BUILDING_WINRT /* probably not useful on WinRT, given current .dll loading restrictions */ +#define SDL_DYNAMIC_API 0 #elif defined(__PS2__) && __PS2__ #define SDL_DYNAMIC_API 0 #elif defined(__PSP__) && __PSP__ diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index ae7717003..586465276 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -571,7 +571,10 @@ #define SDL_GetAssertionHandler SDL_GetAssertionHandler_REAL #define SDL_DXGIGetOutputInfo SDL_DXGIGetOutputInfo_REAL #define SDL_RenderIsClipEnabled SDL_RenderIsClipEnabled_REAL +#define SDL_WinRTRunApp SDL_WinRTRunApp_REAL #define SDL_WarpMouseGlobal SDL_WarpMouseGlobal_REAL +#define SDL_WinRTGetFSPathUNICODE SDL_WinRTGetFSPathUNICODE_REAL +#define SDL_WinRTGetFSPathUTF8 SDL_WinRTGetFSPathUTF8_REAL #define SDL_sqrtf SDL_sqrtf_REAL #define SDL_tan SDL_tan_REAL #define SDL_tanf SDL_tanf_REAL @@ -660,6 +663,7 @@ #define SDL_RenderGetMetalLayer SDL_RenderGetMetalLayer_REAL #define SDL_RenderGetMetalCommandEncoder SDL_RenderGetMetalCommandEncoder_REAL #define SDL_IsAndroidTV SDL_IsAndroidTV_REAL +#define SDL_WinRTGetDeviceFamily SDL_WinRTGetDeviceFamily_REAL #define SDL_log10 SDL_log10_REAL #define SDL_log10f SDL_log10f_REAL #define SDL_GameControllerMappingForDeviceIndex SDL_GameControllerMappingForDeviceIndex_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index d4e2363c7..be2c9379b 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -604,6 +604,11 @@ SDL_DYNAPI_PROC(SDL_AssertionHandler,SDL_GetAssertionHandler,(void **a),(a),retu SDL_DYNAPI_PROC(SDL_bool,SDL_DXGIGetOutputInfo,(int a,int *b, int *c),(a,b,c),return) #endif SDL_DYNAPI_PROC(SDL_bool,SDL_RenderIsClipEnabled,(SDL_Renderer *a),(a),return) +#ifdef __WINRT__ +SDL_DYNAPI_PROC(int,SDL_WinRTRunApp,(int a, char **b, void *c),(a,b,c),return) +SDL_DYNAPI_PROC(const wchar_t*,SDL_WinRTGetFSPathUNICODE,(SDL_WinRT_Path a),(a),return) +SDL_DYNAPI_PROC(const char*,SDL_WinRTGetFSPathUTF8,(SDL_WinRT_Path a),(a),return) +#endif SDL_DYNAPI_PROC(int,SDL_WarpMouseGlobal,(int a, int b),(a,b),return) SDL_DYNAPI_PROC(float,SDL_sqrtf,(float a),(a),return) SDL_DYNAPI_PROC(double,SDL_tan,(double a),(a),return) @@ -695,6 +700,9 @@ SDL_DYNAPI_PROC(SDL_YUV_CONVERSION_MODE,SDL_GetYUVConversionMode,(void),(),retur SDL_DYNAPI_PROC(SDL_YUV_CONVERSION_MODE,SDL_GetYUVConversionModeForResolution,(int a, int b),(a,b),return) SDL_DYNAPI_PROC(void*,SDL_RenderGetMetalLayer,(SDL_Renderer *a),(a),return) SDL_DYNAPI_PROC(void*,SDL_RenderGetMetalCommandEncoder,(SDL_Renderer *a),(a),return) +#ifdef __WINRT__ +SDL_DYNAPI_PROC(SDL_WinRT_DeviceFamily,SDL_WinRTGetDeviceFamily,(void),(),return) +#endif #ifdef __ANDROID__ SDL_DYNAPI_PROC(SDL_bool,SDL_IsAndroidTV,(void),(),return) #endif diff --git a/src/file/SDL_rwops.c b/src/file/SDL_rwops.c index f2ded2ea4..9dc137948 100644 --- a/src/file/SDL_rwops.c +++ b/src/file/SDL_rwops.c @@ -593,6 +593,9 @@ SDL_RWFromFile(const char *file, const char *mode) { #if __APPLE__ && !SDL_FILE_DISABLED // TODO: add dummy? FILE *fp = SDL_OpenFPFromBundleOrFallback(file, mode); + #elif __WINRT__ + FILE *fp = NULL; + fopen_s(&fp, file, mode); #elif __3DS__ FILE *fp = N3DS_FileOpen(file, mode); #else diff --git a/src/filesystem/winrt/SDL_sysfilesystem.cpp b/src/filesystem/winrt/SDL_sysfilesystem.cpp new file mode 100644 index 000000000..115b45b1a --- /dev/null +++ b/src/filesystem/winrt/SDL_sysfilesystem.cpp @@ -0,0 +1,242 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_internal.h" + +/* TODO, WinRT: remove the need to compile this with C++/CX (/ZW) extensions, and if possible, without C++ at all +*/ + +#ifdef __WINRT__ + +extern "C" { +#include "SDL_filesystem.h" +#include "SDL_error.h" +#include "SDL_hints.h" +#include "SDL_stdinc.h" +#include "SDL_system.h" +#include "../../core/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()) { +#if defined(NTDDI_WIN10_19H1) && (NTDDI_VERSION >= NTDDI_WIN10_19H1) && (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) /* Only PC supports mods */ + /* Windows 1903 supports mods, via the EffectiveLocation API */ + if (Windows::Foundation::Metadata::ApiInformation::IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8, 0)) { + path = Windows::ApplicationModel::Package::Current->EffectiveLocation->Path->Data(); + } else { + path = Windows::ApplicationModel::Package::Current->InstalledLocation->Path->Data(); + } +#else + path = Windows::ApplicationModel::Package::Current->InstalledLocation->Path->Data(); +#endif + } + 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) || (NTDDI_VERSION > NTDDI_WIN8) + 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(); +} + +extern "C" char * +SDL_GetBasePath(void) +{ + const char * srcPath = SDL_WinRTGetFSPathUTF8(SDL_WINRT_PATH_INSTALLED_LOCATION); + size_t destPathLen; + char * destPath = NULL; + + if (!srcPath) { + SDL_SetError("Couldn't locate our basepath: %s", SDL_GetError()); + return NULL; + } + + destPathLen = SDL_strlen(srcPath) + 2; + destPath = (char *) SDL_malloc(destPathLen); + if (!destPath) { + SDL_OutOfMemory(); + return NULL; + } + + SDL_snprintf(destPath, destPathLen, "%s\\", srcPath); + return destPath; +} + +extern "C" char * +SDL_GetPrefPath(const char *org, const char *app) +{ + /* WinRT note: The 'SHGetFolderPath' API that is used in Windows 7 and + * earlier is not available on WinRT or Windows Phone. WinRT provides + * a similar API, but SHGetFolderPath can't be called, at least not + * without violating Microsoft's app-store requirements. + */ + + const WCHAR * srcPath = NULL; + WCHAR path[MAX_PATH]; + char *retval = NULL; + WCHAR* worg = NULL; + WCHAR* wapp = NULL; + size_t new_wpath_len = 0; + BOOL api_result = FALSE; + + if (!app) { + SDL_InvalidParamError("app"); + return NULL; + } + if (!org) { + org = ""; + } + + srcPath = SDL_WinRTGetFSPathUNICODE(SDL_WINRT_PATH_LOCAL_FOLDER); + if ( ! srcPath) { + SDL_SetError("Unable to find a source path"); + return NULL; + } + + if (SDL_wcslen(srcPath) >= MAX_PATH) { + SDL_SetError("Path too long."); + return NULL; + } + SDL_wcslcpy(path, srcPath, SDL_arraysize(path)); + + worg = WIN_UTF8ToString(org); + if (worg == NULL) { + SDL_OutOfMemory(); + return NULL; + } + + wapp = WIN_UTF8ToString(app); + if (wapp == NULL) { + SDL_free(worg); + SDL_OutOfMemory(); + return NULL; + } + + new_wpath_len = SDL_wcslen(worg) + SDL_wcslen(wapp) + SDL_wcslen(path) + 3; + + if ((new_wpath_len + 1) > MAX_PATH) { + SDL_free(worg); + SDL_free(wapp); + SDL_SetError("Path too long."); + return NULL; + } + + if (*worg) { + SDL_wcslcat(path, L"\\", new_wpath_len + 1); + SDL_wcslcat(path, worg, new_wpath_len + 1); + SDL_free(worg); + } + + api_result = CreateDirectoryW(path, NULL); + if (api_result == FALSE) { + if (GetLastError() != ERROR_ALREADY_EXISTS) { + SDL_free(wapp); + WIN_SetError("Couldn't create a prefpath."); + return NULL; + } + } + + SDL_wcslcat(path, L"\\", new_wpath_len + 1); + SDL_wcslcat(path, wapp, new_wpath_len + 1); + SDL_free(wapp); + + api_result = CreateDirectoryW(path, NULL); + if (api_result == FALSE) { + if (GetLastError() != ERROR_ALREADY_EXISTS) { + WIN_SetError("Couldn't create a prefpath."); + return NULL; + } + } + + SDL_wcslcat(path, L"\\", new_wpath_len + 1); + + retval = WIN_StringToUTF8(path); + + return retval; +} + +#endif /* __WINRT__ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index 5a53b1b18..de15d5d12 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -436,6 +436,9 @@ SDL_JoystickGetDevicePlayerIndex(int device_index) static SDL_bool SDL_JoystickAxesCenteredAtZero(SDL_Joystick *joystick) { +#ifdef __WINRT__ + return SDL_TRUE; +#else static Uint32 zero_centered_joysticks[] = { MAKE_VIDPID(0x0e8f, 0x3013), /* HuiJia SNES USB adapter */ MAKE_VIDPID(0x05a0, 0x3232), /* 8Bitdo Zero Gamepad */ @@ -458,6 +461,7 @@ SDL_JoystickAxesCenteredAtZero(SDL_Joystick *joystick) } } return SDL_FALSE; +#endif /* __WINRT__ */ } /* diff --git a/src/joystick/windows/SDL_rawinputjoystick.c b/src/joystick/windows/SDL_rawinputjoystick.c index 3a91d606a..fc738b07f 100644 --- a/src/joystick/windows/SDL_rawinputjoystick.c +++ b/src/joystick/windows/SDL_rawinputjoystick.c @@ -577,8 +577,13 @@ RAWINPUT_InitWindowsGamingInput(RAWINPUT_DeviceContext *ctx) typedef HRESULT (WINAPI *WindowsCreateStringReference_t)(PCWSTR sourceString, UINT32 length, HSTRING_HEADER *hstringHeader, HSTRING* string); typedef HRESULT (WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void** factory); +#ifdef __WINRT__ + WindowsCreateStringReference_t WindowsCreateStringReferenceFunc = WindowsCreateStringReference; + RoGetActivationFactory_t RoGetActivationFactoryFunc = RoGetActivationFactory; +#else WindowsCreateStringReference_t WindowsCreateStringReferenceFunc = (WindowsCreateStringReference_t)WIN_LoadComBaseFunction("WindowsCreateStringReference"); RoGetActivationFactory_t RoGetActivationFactoryFunc = (RoGetActivationFactory_t)WIN_LoadComBaseFunction("RoGetActivationFactory"); +#endif if (WindowsCreateStringReferenceFunc && RoGetActivationFactoryFunc) { PCWSTR pNamespace = L"Windows.Gaming.Input.Gamepad"; HSTRING_HEADER hNamespaceStringHeader; diff --git a/src/joystick/windows/SDL_windows_gaming_input.c b/src/joystick/windows/SDL_windows_gaming_input.c index 51eaef6b4..3a89d1154 100644 --- a/src/joystick/windows/SDL_windows_gaming_input.c +++ b/src/joystick/windows/SDL_windows_gaming_input.c @@ -282,9 +282,17 @@ static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeAdde typedef PCWSTR (WINAPI *WindowsGetStringRawBuffer_t)(HSTRING string, UINT32 *length); typedef HRESULT (WINAPI *WindowsDeleteString_t)(HSTRING string); - WindowsGetStringRawBuffer_t WindowsGetStringRawBufferFunc = (WindowsGetStringRawBuffer_t)WIN_LoadComBaseFunction("WindowsGetStringRawBuffer"); - WindowsDeleteString_t WindowsDeleteStringFunc = (WindowsDeleteString_t)WIN_LoadComBaseFunction("WindowsDeleteString"); - + WindowsGetStringRawBuffer_t WindowsGetStringRawBufferFunc = NULL; + WindowsDeleteString_t WindowsDeleteStringFunc = NULL; +#ifdef __WINRT__ + WindowsGetStringRawBufferFunc = WindowsGetStringRawBuffer; + WindowsDeleteStringFunc = WindowsDeleteString; +#else + { + WindowsGetStringRawBufferFunc = (WindowsGetStringRawBuffer_t)WIN_LoadComBaseFunction("WindowsGetStringRawBuffer"); + WindowsDeleteStringFunc = (WindowsDeleteString_t)WIN_LoadComBaseFunction("WindowsDeleteString"); + } +#endif /* __WINRT__ */ if (WindowsGetStringRawBufferFunc && WindowsDeleteStringFunc) { HSTRING hString; hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_get_DisplayName(controller2, &hString); @@ -476,6 +484,7 @@ WGI_JoystickInit(void) } wgi.ro_initialized = SDL_TRUE; +#ifndef __WINRT__ { /* There seems to be a bug in Windows where a dependency of WGI can be unloaded from memory prior to WGI itself. * This results in Windows_Gaming_Input!GameController::~GameController() invoking an unloaded DLL and crashing. @@ -496,9 +505,17 @@ WGI_JoystickInit(void) } } } +#endif - WindowsCreateStringReferenceFunc = (WindowsCreateStringReference_t)WIN_LoadComBaseFunction("WindowsCreateStringReference"); - RoGetActivationFactoryFunc = (RoGetActivationFactory_t)WIN_LoadComBaseFunction("RoGetActivationFactory"); +#ifdef __WINRT__ + WindowsCreateStringReferenceFunc = WindowsCreateStringReference; + RoGetActivationFactoryFunc = RoGetActivationFactory; +#else + { + WindowsCreateStringReferenceFunc = (WindowsCreateStringReference_t)WIN_LoadComBaseFunction("WindowsCreateStringReference"); + RoGetActivationFactoryFunc = (RoGetActivationFactory_t)WIN_LoadComBaseFunction("RoGetActivationFactory"); + } +#endif /* __WINRT__ */ if (WindowsCreateStringReferenceFunc && RoGetActivationFactoryFunc) { PCWSTR pNamespace; HSTRING_HEADER hNamespaceStringHeader; diff --git a/src/joystick/windows/SDL_windowsjoystick.c b/src/joystick/windows/SDL_windowsjoystick.c index 6a4e8ae59..ccd8f37bb 100644 --- a/src/joystick/windows/SDL_windowsjoystick.c +++ b/src/joystick/windows/SDL_windowsjoystick.c @@ -41,7 +41,7 @@ #include "../SDL_sysjoystick.h" #include "../../thread/SDL_systhread.h" #include "../../core/windows/SDL_windows.h" -#if !defined(__XBOXONE__) && !defined(__XBOXSERIES__) +#if !defined(__WINRT__) && !defined(__XBOXONE__) && !defined(__XBOXSERIES__) #include #endif @@ -146,7 +146,7 @@ static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, JoyStick_DeviceData *SYS_Joystick; /* array to hold joystick ID values */ -#if !defined(__XBOXONE__) && !defined(__XBOXSERIES__) +#if !defined(__WINRT__) && !defined(__XBOXONE__) && !defined(__XBOXSERIES__) static HMODULE cfgmgr32_lib_handle; static CM_Register_NotificationFunc CM_Register_Notification; static CM_Unregister_NotificationFunc CM_Unregister_Notification; @@ -332,9 +332,11 @@ SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_mutex *mutex return (lastret != -1) ? SDL_TRUE : SDL_FALSE; } -#endif /* !defined(__XBOXONE__) && !defined(__XBOXSERIES__) */ +#endif /* !defined(__WINRT__) && !defined(__XBOXONE__) && !defined(__XBOXSERIES__) */ +#if !defined(__WINRT__) + #if !defined(__XBOXONE__) && !defined(__XBOXSERIES__) static SDL_DeviceNotificationData s_notification_data; #endif @@ -443,6 +445,8 @@ SDL_StopJoystickThread(void) s_joystickThread = NULL; } +#endif /* !defined(__WINRT__) */ + void WINDOWS_AddJoystickDevice(JoyStick_DeviceData *device) { device->send_add_event = SDL_TRUE; @@ -475,7 +479,7 @@ WINDOWS_JoystickInit(void) WINDOWS_JoystickDetect(); -#if !defined(__XBOXONE__) && !defined(__XBOXSERIES__) +#if !defined(__WINRT__) && !defined(__XBOXONE__) && !defined(__XBOXSERIES__) SDL_CreateDeviceNotificationFunc(); s_bJoystickThread = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_THREAD, SDL_FALSE); @@ -767,7 +771,7 @@ WINDOWS_JoystickQuit(void) } SYS_Joystick = NULL; -#if !defined(__XBOXONE__) && !defined(__XBOXSERIES__) +#if !defined(__WINRT__) && !defined(__XBOXONE__) && !defined(__XBOXSERIES__) if (s_bJoystickThread) { SDL_StopJoystickThread(); } else { diff --git a/src/joystick/windows/SDL_xinputjoystick.c b/src/joystick/windows/SDL_xinputjoystick.c index daeb2d26b..4fa3555b9 100644 --- a/src/joystick/windows/SDL_xinputjoystick.c +++ b/src/joystick/windows/SDL_xinputjoystick.c @@ -46,7 +46,11 @@ static char *s_arrXInputDevicePath[XUSER_MAX_COUNT]; static SDL_bool SDL_XInputUseOldJoystickMapping() { -#if defined(__XBOXONE__) || defined(__XBOXSERIES__) +#ifdef __WINRT__ + /* TODO: remove this __WINRT__ block, but only after integrating with UWP/WinRT's HID API */ + /* FIXME: Why are Win8/10 different here? -flibit */ + return (NTDDI_VERSION < NTDDI_WIN10); +#elif defined(__XBOXONE__) || defined(__XBOXSERIES__) return SDL_FALSE; #else static int s_XInputUseOldJoystickMapping = -1; @@ -129,7 +133,7 @@ GetXInputName(const Uint8 userid, BYTE SubType) static void GuessXInputDevice(Uint8 userid, Uint16 *pVID, Uint16 *pPID, Uint16 *pVersion) { -#if !defined(__XBOXONE__) && !defined(__XBOXSERIES__) /* TODO: remove this ifndef __WINRT__ block, but only after integrating with UWP/WinRT's HID API */ +#if !defined(__WINRT__) && !defined(__XBOXONE__) && !defined(__XBOXSERIES__) /* TODO: remove this ifndef __WINRT__ block, but only after integrating with UWP/WinRT's HID API */ PRAWINPUTDEVICELIST devices = NULL; UINT i, j, device_count = 0; @@ -227,7 +231,7 @@ GuessXInputDevice(Uint8 userid, Uint16 *pVID, Uint16 *pPID, Uint16 *pVersion) } } SDL_free(devices); -#endif +#endif /* !__WINRT__ */ /* The device wasn't in the raw HID device list, it's probably Bluetooth */ *pVID = 0x045e; /* Microsoft */ diff --git a/src/loadso/windows/SDL_sysloadso.c b/src/loadso/windows/SDL_sysloadso.c index c7921da7e..1157ed3b5 100644 --- a/src/loadso/windows/SDL_sysloadso.c +++ b/src/loadso/windows/SDL_sysloadso.c @@ -40,7 +40,15 @@ SDL_LoadObject(const char *sofile) return NULL; } tstr = WIN_UTF8ToString(sofile); +#ifdef __WINRT__ + /* WinRT only publicly supports LoadPackagedLibrary() for loading .dll + files. LoadLibrary() is a private API, and not available for apps + (that can be published to MS' Windows Store.) + */ + handle = (void *) LoadPackagedLibrary(tstr, 0); +#else handle = (void *) LoadLibrary(tstr); +#endif SDL_free(tstr); /* Generate an error message if all loads failed */ diff --git a/src/locale/winrt/SDL_syslocale.c b/src/locale/winrt/SDL_syslocale.c new file mode 100644 index 000000000..1af2a95fc --- /dev/null +++ b/src/locale/winrt/SDL_syslocale.c @@ -0,0 +1,58 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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 + +#include "../../SDL_internal.h" +#include "../SDL_syslocale.h" + +/*using namespace Windows::Graphics::Display;*/ +#include + +void +SDL_SYS_GetPreferredLocales(char *buf, size_t buflen) +{ + WCHAR wbuffer[128] = L""; + int ret = 0; + + /* !!! FIXME: do we not have GetUserPreferredUILanguages on WinPhone or UWP? */ +# if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP + ret = GetLocaleInfoEx(LOCALE_NAME_SYSTEM_DEFAULT, LOCALE_SNAME, wbuffer, SDL_arraysize(wbuffer)); +# else + ret = GetSystemDefaultLocaleName(wbuffer, SDL_arraysize(wbuffer)); +# endif + + if (ret > 0) + { + /* Need to convert LPWSTR to LPSTR, that is wide char to char. */ + int i; + + if ( ((size_t) ret) >= (buflen - 1) ) { + ret = (int) (buflen - 1); + } + for (i = 0; i < ret; i++) { + buf[i] = (char) wbuffer[i]; /* assume this was ASCII anyhow. */ + } + } +} + +/* vi: set ts=4 sw=4 expandtab: */ + diff --git a/src/main/winrt/SDL3-WinRTResource_BlankCursor.cur b/src/main/winrt/SDL3-WinRTResource_BlankCursor.cur new file mode 100644 index 000000000..e69de29bb diff --git a/src/main/winrt/SDL3-WinRTResources.rc b/src/main/winrt/SDL3-WinRTResources.rc new file mode 100644 index 000000000..457fd093b --- /dev/null +++ b/src/main/winrt/SDL3-WinRTResources.rc @@ -0,0 +1,3 @@ +#include "winres.h" +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +5000 CURSOR "SDL3-WinRTResource_BlankCursor.cur" diff --git a/src/main/winrt/SDL_winrt_main_NonXAML.cpp b/src/main/winrt/SDL_winrt_main_NonXAML.cpp new file mode 100644 index 000000000..19d22504c --- /dev/null +++ b/src/main/winrt/SDL_winrt_main_NonXAML.cpp @@ -0,0 +1,54 @@ +/* + SDL_winrt_main_NonXAML.cpp, placed in the public domain by David Ludwig 3/13/14 +*/ + +#include "SDL_main.h" +#include + +/* At least one file in any SDL/WinRT app appears to require compilation + with C++/CX, otherwise a Windows Metadata file won't get created, and + an APPX0702 build error can appear shortly after linking. + + The following set of preprocessor code forces this file to be compiled + as C++/CX, which appears to cause Visual C++ 2012's build tools to + create this .winmd file, and will help allow builds of SDL/WinRT apps + to proceed without error. + + If other files in an app's project enable C++/CX compilation, then it might + be possible for SDL_winrt_main_NonXAML.cpp to be compiled without /ZW, + for Visual C++'s build tools to create a winmd file, and for the app to + build without APPX0702 errors. In this case, if + SDL_WINRT_METADATA_FILE_AVAILABLE is defined as a C/C++ macro, then + the #error (to force C++/CX compilation) will be disabled. + + Please note that /ZW can be specified on a file-by-file basis. To do this, + right click on the file in Visual C++, click Properties, then change the + setting through the dialog that comes up. +*/ +#ifndef SDL_WINRT_METADATA_FILE_AVAILABLE +#ifndef __cplusplus_winrt +#error SDL_winrt_main_NonXAML.cpp must be compiled with /ZW, otherwise build errors due to missing .winmd files can occur. +#endif +#endif + +/* Prevent MSVC++ from warning about threading models when defining our + custom WinMain. The threading model will instead be set via a direct + call to Windows::Foundation::Initialize (rather than via an attributed + function). + + To note, this warning (C4447) does not seem to come up unless this file + is compiled with C++/CX enabled (via the /ZW compiler flag). +*/ +#ifdef _MSC_VER +#pragma warning(disable:4447) +#endif + +/* Make sure the function to initialize the Windows Runtime gets linked in. */ +#ifdef _MSC_VER +#pragma comment(lib, "runtimeobject.lib") +#endif + +int CALLBACK WinMain(HINSTANCE, HINSTANCE, LPSTR, int) +{ + return SDL_WinRTRunApp(SDL_main, NULL); +} diff --git a/src/misc/winrt/SDL_sysurl.cpp b/src/misc/winrt/SDL_sysurl.cpp new file mode 100644 index 000000000..111a14413 --- /dev/null +++ b/src/misc/winrt/SDL_sysurl.cpp @@ -0,0 +1,41 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_sysurl.h" +#include "../../core/windows/SDL_windows.h" + +int +SDL_SYS_OpenURL(const char *url) +{ + WCHAR *wurl = WIN_UTF8ToStringW(url); + if (!wurl) { + return SDL_OutOfMemory(); + } + auto strurl = ref new Platform::String(wurl); + SDL_free(wurl); + + auto uri = ref new Windows::Foundation::Uri(strurl); + Windows::System::Launcher::LaunchUriAsync(uri); + return 0; // oh well, we're not waiting on an async task here. +} + +/* vi: set ts=4 sw=4 expandtab: */ + diff --git a/src/power/SDL_power.c b/src/power/SDL_power.c index 4f1b4b224..fbf8d5a65 100644 --- a/src/power/SDL_power.c +++ b/src/power/SDL_power.c @@ -74,6 +74,9 @@ static SDL_GetPowerInfo_Impl implementations[] = { #ifdef SDL_POWER_N3DS /* handles N3DS. */ SDL_GetPowerInfo_N3DS, #endif +#ifdef SDL_POWER_WINRT /* handles WinRT */ + SDL_GetPowerInfo_WinRT, +#endif #ifdef SDL_POWER_EMSCRIPTEN /* handles Emscripten */ SDL_GetPowerInfo_Emscripten, #endif diff --git a/src/power/SDL_syspower.h b/src/power/SDL_syspower.h index 4ac8b197f..2f81756c4 100644 --- a/src/power/SDL_syspower.h +++ b/src/power/SDL_syspower.h @@ -40,6 +40,7 @@ SDL_bool SDL_GetPowerInfo_Android(SDL_PowerState *, int *, int *); SDL_bool SDL_GetPowerInfo_PSP(SDL_PowerState *, int *, int *); SDL_bool SDL_GetPowerInfo_VITA(SDL_PowerState *, int *, int *); SDL_bool SDL_GetPowerInfo_N3DS(SDL_PowerState *, int *, int *); +SDL_bool SDL_GetPowerInfo_WinRT(SDL_PowerState *, int *, int *); SDL_bool SDL_GetPowerInfo_Emscripten(SDL_PowerState *, int *, int *); /* this one is static in SDL_power.c */ diff --git a/src/power/winrt/SDL_syspower.cpp b/src/power/winrt/SDL_syspower.cpp new file mode 100644 index 000000000..62e7ea6d6 --- /dev/null +++ b/src/power/winrt/SDL_syspower.cpp @@ -0,0 +1,44 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_internal.h" + +#ifndef SDL_POWER_DISABLED +#if SDL_POWER_WINRT + +#include "SDL_power.h" + +extern "C" +SDL_bool +SDL_GetPowerInfo_WinRT(SDL_PowerState * state, int *seconds, int *percent) +{ + /* TODO, WinRT: Battery info is available on at least one WinRT platform (Windows Phone 8). Implement SDL_GetPowerInfo_WinRT as appropriate. */ + /* Notes: + - the Win32 function, GetSystemPowerStatus, is not available for use on WinRT + - Windows Phone 8 has a 'Battery' class, which is documented as available for C++ + - More info on WP8's Battery class can be found at http://msdn.microsoft.com/library/windowsphone/develop/jj207231 + */ + return SDL_FALSE; +} + +#endif /* SDL_POWER_WINRT */ +#endif /* SDL_POWER_DISABLED */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index 585932674..8369e54f6 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -27,7 +27,9 @@ #define COBJMACROS #include "../../core/windows/SDL_windows.h" +#if !defined(__WINRT__) #include "../../video/windows/SDL_windowswindow.h" +#endif #include "SDL_hints.h" #include "SDL_loadso.h" #include "SDL_syswm.h" @@ -38,6 +40,22 @@ #include "SDL_shaders_d3d11.h" +#ifdef __WINRT__ + +#if NTDDI_VERSION > NTDDI_WIN8 +#include +#endif + +#include "SDL_render_winrt.h" + +#if WINAPI_FAMILY == WINAPI_FAMILY_APP +#include +/* TODO, WinRT, XAML: get the ISwapChainBackgroundPanelNative from something other than a global var */ +extern ISwapChainBackgroundPanelNative * WINRT_GlobalSwapChainBackgroundPanelNative; +#endif /* WINAPI_FAMILY == WINAPI_FAMILY_APP */ + +#endif /* __WINRT__ */ + #if defined(_MSC_VER) && !defined(__clang__) #define SDL_COMPOSE_ERROR(str) __FUNCTION__ ", " str @@ -169,6 +187,9 @@ typedef struct static const GUID SDL_IID_IDXGIFactory2 = { 0x50c83a1c, 0xe072, 0x4c48, { 0x87, 0xb0, 0x36, 0x30, 0xfa, 0x36, 0xa6, 0xd0 } }; static const GUID SDL_IID_IDXGIDevice1 = { 0x77db970f, 0x6276, 0x48ba, { 0xba, 0x28, 0x07, 0x01, 0x43, 0xb4, 0x39, 0x2c } }; +#if defined(__WINRT__) && NTDDI_VERSION > NTDDI_WIN8 +static const GUID SDL_IID_IDXGIDevice3 = { 0x6007896c, 0x3244, 0x4afd, { 0xbf, 0x18, 0xa6, 0xd3, 0xbe, 0xda, 0x50, 0x23 } }; +#endif static const GUID SDL_IID_ID3D11Texture2D = { 0x6f15aaf2, 0xd208, 0x4e89, { 0x9a, 0xb4, 0x48, 0x95, 0x35, 0xd3, 0x4f, 0x9c } }; static const GUID SDL_IID_ID3D11Device1 = { 0xa04bfb29, 0x08ef, 0x43d6, { 0xa4, 0x9c, 0xa9, 0xbd, 0xbd, 0xcb, 0xe6, 0x86 } }; static const GUID SDL_IID_ID3D11DeviceContext1 = { 0xbb2c6faa, 0xb5fb, 0x4082, { 0x8e, 0x6b, 0x38, 0x8b, 0x8c, 0xfa, 0x90, 0xe1 } }; @@ -418,6 +439,10 @@ D3D11_CreateDeviceResources(SDL_Renderer * renderer) D3D11_SAMPLER_DESC samplerDesc; D3D11_RASTERIZER_DESC rasterDesc; +#ifdef __WINRT__ + CreateDXGIFactoryFunc = CreateDXGIFactory1; + D3D11CreateDeviceFunc = D3D11CreateDevice; +#else data->hDXGIMod = SDL_LoadObject("dxgi.dll"); if (!data->hDXGIMod) { result = E_FAIL; @@ -441,6 +466,7 @@ D3D11_CreateDeviceResources(SDL_Renderer * renderer) result = E_FAIL; goto done; } +#endif /* __WINRT__ */ result = CreateDXGIFactoryFunc(&SDL_IID_IDXGIFactory2, (void **)&data->dxgiFactory); if (FAILED(result)) { @@ -728,8 +754,13 @@ static HRESULT D3D11_CreateSwapChain(SDL_Renderer * renderer, int w, int h) { D3D11_RenderData *data = (D3D11_RenderData *)renderer->driverdata; +#ifdef __WINRT__ + IUnknown *coreWindow = D3D11_GetCoreWindowFromSDLRenderer(renderer); + const BOOL usingXAML = (coreWindow == NULL); +#else IUnknown *coreWindow = NULL; const BOOL usingXAML = FALSE; +#endif HRESULT result = S_OK; /* Create a swap chain using the same adapter as the existing Direct3D device. */ @@ -882,7 +913,11 @@ D3D11_CreateWindowSizeDependentResources(SDL_Renderer * renderer) /* The width and height of the swap chain must be based on the display's * non-rotated size. */ +#if defined(__WINRT__) + SDL_GetWindowSize(renderer->window, &w, &h); +#else SDL_GetWindowSizeInPixels(renderer->window, &w, &h); +#endif data->rotation = D3D11_GetCurrentRotation(); /* SDL_Log("%s: windowSize={%d,%d}, orientation=%d\n", __FUNCTION__, w, h, (int)data->rotation); */ if (D3D11_IsDisplayRotated90Degrees(data->rotation)) { @@ -893,7 +928,7 @@ D3D11_CreateWindowSizeDependentResources(SDL_Renderer * renderer) if (data->swapChain) { /* IDXGISwapChain::ResizeBuffers is not available on Windows Phone 8. */ -#if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP) +#if !defined(__WINRT__) || (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP) /* If the swap chain already exists, resize it. */ result = IDXGISwapChain_ResizeBuffers(data->swapChain, 0, @@ -997,6 +1032,22 @@ D3D11_UpdateForWindowSizeChange(SDL_Renderer * renderer) void D3D11_Trim(SDL_Renderer * renderer) { +#ifdef __WINRT__ +#if NTDDI_VERSION > NTDDI_WIN8 + D3D11_RenderData *data = (D3D11_RenderData *)renderer->driverdata; + HRESULT result = S_OK; + IDXGIDevice3 *dxgiDevice = NULL; + + result = ID3D11Device_QueryInterface(data->d3dDevice, &SDL_IID_IDXGIDevice3, &dxgiDevice); + if (FAILED(result)) { + //WIN_SetErrorFromHRESULT(__FUNCTION__ ", ID3D11Device to IDXGIDevice3", result); + return; + } + + IDXGIDevice3_Trim(dxgiDevice); + SAFE_RELEASE(dxgiDevice); +#endif +#endif } static void @@ -1007,12 +1058,14 @@ D3D11_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) } } +#if !defined(__WINRT__) static int D3D11_GetOutputSize(SDL_Renderer * renderer, int *w, int *h) { SDL_GetWindowSizeInPixels(renderer->window, w, h); return 0; } +#endif static SDL_bool D3D11_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode) @@ -2331,7 +2384,9 @@ D3D11_CreateRenderer(SDL_Window * window, Uint32 flags) data->identity = MatrixIdentity(); renderer->WindowEvent = D3D11_WindowEvent; +#if !defined(__WINRT__) renderer->GetOutputSize = D3D11_GetOutputSize; +#endif renderer->SupportsBlendMode = D3D11_SupportsBlendMode; renderer->CreateTexture = D3D11_CreateTexture; renderer->UpdateTexture = D3D11_UpdateTexture; diff --git a/src/render/direct3d11/SDL_render_winrt.cpp b/src/render/direct3d11/SDL_render_winrt.cpp new file mode 100644 index 000000000..9784c32c4 --- /dev/null +++ b/src/render/direct3d11/SDL_render_winrt.cpp @@ -0,0 +1,116 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_internal.h" + +#if SDL_VIDEO_RENDER_D3D11 && !SDL_RENDER_DISABLED + +#include "SDL_syswm.h" +#include "../../video/winrt/SDL_winrtvideo_cpp.h" +extern "C" { +#include "../SDL_sysrender.h" +} + +#include +#include + +#if WINAPI_FAMILY == WINAPI_FAMILY_APP +#include +#endif + +using namespace Windows::UI::Core; +using namespace Windows::Graphics::Display; + +#include + +#include "SDL_render_winrt.h" + + +extern "C" void * +D3D11_GetCoreWindowFromSDLRenderer(SDL_Renderer * renderer) +{ + SDL_Window * sdlWindow = renderer->window; + if ( ! renderer->window ) { + return NULL; + } + + SDL_SysWMinfo sdlWindowInfo; + SDL_VERSION(&sdlWindowInfo.version); + if ( ! SDL_GetWindowWMInfo(sdlWindow, &sdlWindowInfo) ) { + return NULL; + } + + if (sdlWindowInfo.subsystem != SDL_SYSWM_WINRT) { + return NULL; + } + + if (!sdlWindowInfo.info.winrt.window) { + return NULL; + } + + ABI::Windows::UI::Core::ICoreWindow *coreWindow = NULL; + if (FAILED(sdlWindowInfo.info.winrt.window->QueryInterface(&coreWindow))) { + return NULL; + } + + IUnknown *coreWindowAsIUnknown = NULL; + coreWindow->QueryInterface(&coreWindowAsIUnknown); + coreWindow->Release(); + + return coreWindowAsIUnknown; +} + +extern "C" DXGI_MODE_ROTATION +D3D11_GetCurrentRotation() +{ + const DisplayOrientations currentOrientation = WINRT_DISPLAY_PROPERTY(CurrentOrientation); + + switch (currentOrientation) { + +#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP + /* Windows Phone rotations */ + case DisplayOrientations::Landscape: + return DXGI_MODE_ROTATION_ROTATE90; + case DisplayOrientations::Portrait: + return DXGI_MODE_ROTATION_IDENTITY; + case DisplayOrientations::LandscapeFlipped: + return DXGI_MODE_ROTATION_ROTATE270; + case DisplayOrientations::PortraitFlipped: + return DXGI_MODE_ROTATION_ROTATE180; +#else + /* Non-Windows-Phone rotations (ex: Windows 8, Windows RT) */ + case DisplayOrientations::Landscape: + return DXGI_MODE_ROTATION_IDENTITY; + case DisplayOrientations::Portrait: + return DXGI_MODE_ROTATION_ROTATE270; + case DisplayOrientations::LandscapeFlipped: + return DXGI_MODE_ROTATION_ROTATE180; + case DisplayOrientations::PortraitFlipped: + return DXGI_MODE_ROTATION_ROTATE90; +#endif /* WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP */ + } + + return DXGI_MODE_ROTATION_IDENTITY; +} + + +#endif /* SDL_VIDEO_RENDER_D3D11 && !SDL_RENDER_DISABLED */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/direct3d11/SDL_render_winrt.h b/src/render/direct3d11/SDL_render_winrt.h new file mode 100644 index 000000000..97d27cb27 --- /dev/null +++ b/src/render/direct3d11/SDL_render_winrt.h @@ -0,0 +1,40 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_internal.h" + +#if SDL_VIDEO_RENDER_D3D11 && !SDL_RENDER_DISABLED + +#include "SDL_render.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void * D3D11_GetCoreWindowFromSDLRenderer(SDL_Renderer * renderer); +DXGI_MODE_ROTATION D3D11_GetCurrentRotation(); + +#ifdef __cplusplus +} +#endif + +#endif /* SDL_VIDEO_RENDER_D3D11 && !SDL_RENDER_DISABLED */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index dd654f480..da48e4d6e 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -2152,6 +2152,14 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags) goto error; } +#if __WINRT__ + /* DLudwig, 2013-11-29: ANGLE for WinRT doesn't seem to work unless VSync + * is turned on. Not doing so will freeze the screen's contents to that + * of the first drawn frame. + */ + flags |= SDL_RENDERER_PRESENTVSYNC; +#endif + if (flags & SDL_RENDERER_PRESENTVSYNC) { SDL_GL_SetSwapInterval(1); } else { diff --git a/src/thread/stdcpp/SDL_systhread.cpp b/src/thread/stdcpp/SDL_systhread.cpp index 45fa465ed..3d818ac56 100644 --- a/src/thread/stdcpp/SDL_systhread.cpp +++ b/src/thread/stdcpp/SDL_systhread.cpp @@ -32,6 +32,10 @@ extern "C" { #include #include +#ifdef __WINRT__ +#include +#endif + static void RunThread(void *args) { @@ -67,6 +71,9 @@ extern "C" SDL_threadID SDL_ThreadID(void) { +#ifdef __WINRT__ + return GetCurrentThreadId(); +#else // HACK: Mimick a thread ID, if one isn't otherwise available. static thread_local SDL_threadID current_thread_id = 0; static SDL_threadID next_thread_id = 1; @@ -79,13 +86,37 @@ SDL_ThreadID(void) } return current_thread_id; +#endif } extern "C" int SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority) { +#ifdef __WINRT__ + int value; + + if (priority == SDL_THREAD_PRIORITY_LOW) { + value = THREAD_PRIORITY_LOWEST; + } + else if (priority == SDL_THREAD_PRIORITY_HIGH) { + value = THREAD_PRIORITY_HIGHEST; + } + else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) { + // FIXME: WinRT does not support TIME_CRITICAL! -flibit + SDL_LogWarn(SDL_LOG_CATEGORY_SYSTEM, "TIME_CRITICAL unsupported, falling back to HIGHEST"); + value = THREAD_PRIORITY_HIGHEST; + } + else { + value = THREAD_PRIORITY_NORMAL; + } + if (!SetThreadPriority(GetCurrentThread(), value)) { + return WIN_SetError("SetThreadPriority()"); + } + return 0; +#else return SDL_Unsupported(); +#endif } extern "C" diff --git a/src/thread/windows/SDL_syscond_cv.c b/src/thread/windows/SDL_syscond_cv.c index fea2ac57c..9c0ceb3a8 100644 --- a/src/thread/windows/SDL_syscond_cv.c +++ b/src/thread/windows/SDL_syscond_cv.c @@ -58,6 +58,12 @@ typedef struct CONDITION_VARIABLE { } CONDITION_VARIABLE, *PCONDITION_VARIABLE; #endif +#if __WINRT__ +#define pWakeConditionVariable WakeConditionVariable +#define pWakeAllConditionVariable WakeAllConditionVariable +#define pSleepConditionVariableSRW SleepConditionVariableSRW +#define pSleepConditionVariableCS SleepConditionVariableCS +#else typedef VOID(WINAPI *pfnWakeConditionVariable)(PCONDITION_VARIABLE); typedef VOID(WINAPI *pfnWakeAllConditionVariable)(PCONDITION_VARIABLE); typedef BOOL(WINAPI *pfnSleepConditionVariableSRW)(PCONDITION_VARIABLE, PSRWLOCK, DWORD, ULONG); @@ -67,6 +73,7 @@ static pfnWakeConditionVariable pWakeConditionVariable = NULL; static pfnWakeAllConditionVariable pWakeAllConditionVariable = NULL; static pfnSleepConditionVariableSRW pSleepConditionVariableSRW = NULL; static pfnSleepConditionVariableCS pSleepConditionVariableCS = NULL; +#endif typedef struct SDL_cond_cv { @@ -235,6 +242,10 @@ SDL_CreateCond(void) SDL_assert(SDL_mutex_impl_active.Type != SDL_MUTEX_INVALID); } +#if __WINRT__ + /* Link statically on this platform */ + impl = &SDL_cond_impl_cv; +#else { HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll")); if (kernel32) { @@ -248,6 +259,7 @@ SDL_CreateCond(void) } } } +#endif SDL_copyp(&SDL_cond_impl_active, impl); } diff --git a/src/thread/windows/SDL_sysmutex.c b/src/thread/windows/SDL_sysmutex.c index 793964384..3974b3362 100644 --- a/src/thread/windows/SDL_sysmutex.c +++ b/src/thread/windows/SDL_sysmutex.c @@ -44,12 +44,19 @@ SDL_mutex_impl_t SDL_mutex_impl_active = {0}; * Implementation based on Slim Reader/Writer (SRW) Locks for Win 7 and newer. */ +#if __WINRT__ +/* Functions are guaranteed to be available */ +#define pReleaseSRWLockExclusive ReleaseSRWLockExclusive +#define pAcquireSRWLockExclusive AcquireSRWLockExclusive +#define pTryAcquireSRWLockExclusive TryAcquireSRWLockExclusive +#else typedef VOID(WINAPI *pfnReleaseSRWLockExclusive)(PSRWLOCK); typedef VOID(WINAPI *pfnAcquireSRWLockExclusive)(PSRWLOCK); typedef BOOLEAN(WINAPI *pfnTryAcquireSRWLockExclusive)(PSRWLOCK); static pfnReleaseSRWLockExclusive pReleaseSRWLockExclusive = NULL; static pfnAcquireSRWLockExclusive pAcquireSRWLockExclusive = NULL; static pfnTryAcquireSRWLockExclusive pTryAcquireSRWLockExclusive = NULL; +#endif static SDL_mutex * SDL_CreateMutex_srw(void) @@ -173,7 +180,11 @@ SDL_CreateMutex_cs(void) if (mutex) { /* Initialize */ /* On SMP systems, a non-zero spin count generally helps performance */ +#if __WINRT__ + InitializeCriticalSectionEx(&mutex->cs, 2000, 0); +#else InitializeCriticalSectionAndSpinCount(&mutex->cs, 2000); +#endif } else { SDL_OutOfMemory(); } @@ -256,6 +267,10 @@ SDL_CreateMutex(void) const SDL_mutex_impl_t * impl = &SDL_mutex_impl_cs; if (!SDL_GetHintBoolean(SDL_HINT_WINDOWS_FORCE_MUTEX_CRITICAL_SECTIONS, SDL_FALSE)) { +#if __WINRT__ + /* Link statically on this platform */ + impl = &SDL_mutex_impl_srw; +#else /* Try faster implementation for Windows 7 and newer */ HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll")); if (kernel32) { @@ -268,6 +283,7 @@ SDL_CreateMutex(void) impl = &SDL_mutex_impl_srw; } } +#endif } /* Copy instead of using pointer to save one level of indirection */ diff --git a/src/thread/windows/SDL_syssem.c b/src/thread/windows/SDL_syssem.c index 7e945ddbd..629f2a9cc 100644 --- a/src/thread/windows/SDL_syssem.c +++ b/src/thread/windows/SDL_syssem.c @@ -76,11 +76,17 @@ static SDL_sem_impl_t SDL_sem_impl_active = {0}; #endif #if !SDL_WINAPI_FAMILY_PHONE +#if __WINRT__ +/* Functions are guaranteed to be available */ +#define pWaitOnAddress WaitOnAddress +#define pWakeByAddressSingle WakeByAddressSingle +#else typedef BOOL(WINAPI *pfnWaitOnAddress)(volatile VOID*, PVOID, SIZE_T, DWORD); typedef VOID(WINAPI *pfnWakeByAddressSingle)(PVOID); static pfnWaitOnAddress pWaitOnAddress = NULL; static pfnWakeByAddressSingle pWakeByAddressSingle = NULL; +#endif typedef struct SDL_semaphore_atom { @@ -268,7 +274,11 @@ SDL_CreateSemaphore_kern(Uint32 initial_value) sem = (SDL_sem_kern *) SDL_malloc(sizeof(*sem)); if (sem) { /* Create the semaphore, with max value 32K */ +#if __WINRT__ + sem->id = CreateSemaphoreEx(NULL, initial_value, 32 * 1024, NULL, 0, SEMAPHORE_ALL_ACCESS); +#else sem->id = CreateSemaphore(NULL, initial_value, 32 * 1024, NULL); +#endif sem->count = initial_value; if (!sem->id) { SDL_SetError("Couldn't create semaphore"); @@ -395,6 +405,10 @@ SDL_CreateSemaphore(Uint32 initial_value) #if !SDL_WINAPI_FAMILY_PHONE if (!SDL_GetHintBoolean(SDL_HINT_WINDOWS_FORCE_SEMAPHORE_KERNEL, SDL_FALSE)) { +#if __WINRT__ + /* Link statically on this platform */ + impl = &SDL_sem_impl_atom; +#else /* We already statically link to features from this Api * Set (e.g. WaitForSingleObject). Dynamically loading * API Sets is not explicitly documented but according to @@ -411,6 +425,7 @@ SDL_CreateSemaphore(Uint32 initial_value) impl = &SDL_sem_impl_atom; } } +#endif } #endif diff --git a/src/thread/windows/SDL_systhread.c b/src/thread/windows/SDL_systhread.c index 910c346ff..3701cf01f 100644 --- a/src/thread/windows/SDL_systhread.c +++ b/src/thread/windows/SDL_systhread.c @@ -87,7 +87,7 @@ SDL_SYS_CreateThread(SDL_Thread * thread, pfnSDL_CurrentBeginThread pfnBeginThread, pfnSDL_CurrentEndThread pfnEndThread) { -#elif defined(__CYGWIN__) +#elif defined(__CYGWIN__) || defined(__WINRT__) int SDL_SYS_CreateThread(SDL_Thread * thread) { @@ -141,6 +141,7 @@ void SDL_SYS_SetupThread(const char *name) { if (name != NULL) { + #ifndef __WINRT__ /* !!! FIXME: There's no LoadLibrary() in WinRT; don't know if SetThreadDescription is available there at all at the moment. */ static pfnSetThreadDescription pSetThreadDescription = NULL; static HMODULE kernel32 = 0; @@ -158,6 +159,7 @@ SDL_SYS_SetupThread(const char *name) SDL_free(strw); } } + #endif /* Presumably some version of Visual Studio will understand SetThreadDescription(), but we still need to deal with older OSes and debuggers. Set it with the arcane diff --git a/src/thread/windows/SDL_systls.c b/src/thread/windows/SDL_systls.c index 2567a1411..22e507a7a 100644 --- a/src/thread/windows/SDL_systls.c +++ b/src/thread/windows/SDL_systls.c @@ -28,6 +28,18 @@ #include "SDL_thread.h" #include "../SDL_thread_c.h" +#if WINAPI_FAMILY_WINRT +#include + +#ifndef TLS_OUT_OF_INDEXES +#define TLS_OUT_OF_INDEXES FLS_OUT_OF_INDEXES +#endif + +#define TlsAlloc() FlsAlloc(NULL) +#define TlsSetValue FlsSetValue +#define TlsGetValue FlsGetValue +#endif + static DWORD thread_local_storage = TLS_OUT_OF_INDEXES; static SDL_bool generic_local_storage = SDL_FALSE; diff --git a/src/timer/windows/SDL_systimer.c b/src/timer/windows/SDL_systimer.c index 7c8d50ab1..52dea837f 100644 --- a/src/timer/windows/SDL_systimer.c +++ b/src/timer/windows/SDL_systimer.c @@ -41,7 +41,7 @@ static LARGE_INTEGER ticks_per_second; static void SDL_SetSystemTimerResolution(const UINT uPeriod) { -#if !defined(__XBOXONE__) && !defined(__XBOXSERIES__) +#if !defined(__WINRT__) && !defined(__XBOXONE__) && !defined(__XBOXSERIES__) static UINT timer_period = 0; if (uPeriod != timer_period) { @@ -146,11 +146,31 @@ SDL_GetPerformanceFrequency(void) void SDL_Delay(Uint32 ms) { + /* Sleep() is not publicly available to apps in early versions of WinRT. + * + * Visual C++ 2013 Update 4 re-introduced Sleep() for Windows 8.1 and + * Windows Phone 8.1. + * + * Use the compiler version to determine availability. + * + * NOTE #1: _MSC_FULL_VER == 180030723 for Visual C++ 2013 Update 3. + * NOTE #2: Visual C++ 2013, when compiling for Windows 8.0 and + * Windows Phone 8.0, uses the Visual C++ 2012 compiler to build + * apps and libraries. + */ +#if defined(__WINRT__) && defined(_MSC_FULL_VER) && (_MSC_FULL_VER <= 180030723) + static HANDLE mutex = 0; + if (!mutex) { + mutex = CreateEventEx(0, 0, 0, EVENT_ALL_ACCESS); + } + WaitForSingleObjectEx(mutex, ms, FALSE); +#else if (!ticks_started) { SDL_TicksInit(); } Sleep(ms); +#endif } #endif /* SDL_TIMER_WINDOWS */ diff --git a/src/video/SDL_egl.c b/src/video/SDL_egl.c index 822ead92c..41ae6ec01 100644 --- a/src/video/SDL_egl.c +++ b/src/video/SDL_egl.c @@ -22,7 +22,7 @@ #if SDL_VIDEO_OPENGL_EGL -#if SDL_VIDEO_DRIVER_WINDOWS +#if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT #include "../core/windows/SDL_windows.h" #endif #if SDL_VIDEO_DRIVER_ANDROID @@ -73,7 +73,7 @@ #define DEFAULT_OGL_ES_PVR "libGLES_CM.so" #define DEFAULT_OGL_ES "libGLESv1_CM.so" -#elif SDL_VIDEO_DRIVER_WINDOWS +#elif SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT /* EGL AND OpenGL ES support via ANGLE */ #define DEFAULT_EGL "libEGL.dll" #define DEFAULT_OGL_ES2 "libGLESv2.dll" @@ -297,14 +297,14 @@ SDL_EGL_LoadLibraryInternal(_THIS, const char *egl_path) { void *egl_dll_handle = NULL, *opengl_dll_handle = NULL; const char *path = NULL; -#if SDL_VIDEO_DRIVER_WINDOWS +#if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT const char *d3dcompiler; #endif #if SDL_VIDEO_DRIVER_RPI SDL_bool vc4 = (0 == access("/sys/module/vc4/", F_OK)); #endif -#if SDL_VIDEO_DRIVER_WINDOWS +#if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT d3dcompiler = SDL_GetHint(SDL_HINT_VIDEO_WIN_D3DCOMPILER); if (d3dcompiler) { if (SDL_strcasecmp(d3dcompiler, "none") != 0) { @@ -510,6 +510,7 @@ SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_displa _this->egl_data->egl_display = EGL_NO_DISPLAY; +#if !defined(__WINRT__) #if !defined(SDL_VIDEO_DRIVER_VITA) if (platform) { /* EGL 1.5 allows querying for client version with EGL_NO_DISPLAY @@ -551,6 +552,7 @@ SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_displa *_this->gl_config.driver_path = '\0'; return SDL_SetError("Could not initialize EGL"); } +#endif /* Get the EGL version with a valid egl_display, for EGL <= 1.4 */ SDL_EGL_GetVersion(_this); diff --git a/src/video/SDL_stretch.c b/src/video/SDL_stretch.c index 5f6a68d37..bc7b4e551 100644 --- a/src/video/SDL_stretch.c +++ b/src/video/SDL_stretch.c @@ -352,7 +352,7 @@ scale_mat(const Uint32 *src, int src_w, int src_h, int src_pitch, # define CAST_uint32x2_t (uint32x2_t) #endif -#if defined(_MSC_VER) +#if defined(__WINRT__) || defined(_MSC_VER) # if defined(HAVE_NEON_INTRINSICS) # undef CAST_uint8x8_t # undef CAST_uint32x2_t diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index d5a2f0022..5a0c24d62 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -451,6 +451,7 @@ typedef struct VideoBootStrap extern VideoBootStrap COCOA_bootstrap; extern VideoBootStrap X11_bootstrap; extern VideoBootStrap WINDOWS_bootstrap; +extern VideoBootStrap WINRT_bootstrap; extern VideoBootStrap HAIKU_bootstrap; extern VideoBootStrap PND_bootstrap; extern VideoBootStrap UIKIT_bootstrap; diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 9839c580b..e25cb2dab 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -80,6 +80,9 @@ static VideoBootStrap *bootstrap[] = { #if SDL_VIDEO_DRIVER_WINDOWS &WINDOWS_bootstrap, #endif +#if SDL_VIDEO_DRIVER_WINRT + &WINRT_bootstrap, +#endif #if SDL_VIDEO_DRIVER_HAIKU &HAIKU_bootstrap, #endif @@ -1336,6 +1339,10 @@ SDL_RestoreMousePosition(SDL_Window *window) } } +#if __WINRT__ +extern Uint32 WINRT_DetectWindowFlags(SDL_Window * window); +#endif + static int SDL_UpdateFullscreenMode(SDL_Window * window, SDL_bool fullscreen) { @@ -1378,6 +1385,30 @@ SDL_UpdateFullscreenMode(SDL_Window * window, SDL_bool fullscreen) return 0; } } +#elif __WINRT__ && (NTDDI_VERSION < NTDDI_WIN10) + /* HACK: WinRT 8.x apps can't choose whether or not they are fullscreen + or not. The user can choose this, via OS-provided UI, but this can't + be set programmatically. + + Just look at what SDL's WinRT video backend code detected with regards + to fullscreen (being active, or not), and figure out a return/error code + from that. + */ + if (fullscreen == !(WINRT_DetectWindowFlags(window) & FULLSCREEN_MASK)) { + /* Uh oh, either: + 1. fullscreen was requested, and we're already windowed + 2. windowed-mode was requested, and we're already fullscreen + + WinRT 8.x can't resolve either programmatically, so we're + giving up. + */ + return -1; + } else { + /* Whatever was requested, fullscreen or windowed mode, is already + in-place. + */ + return 0; + } #endif display = SDL_GetDisplayForWindow(window); @@ -1731,6 +1762,18 @@ SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags) } #endif +#if __WINRT__ && (NTDDI_VERSION < NTDDI_WIN10) + /* HACK: WinRT 8.x apps can't choose whether or not they are fullscreen + or not. The user can choose this, via OS-provided UI, but this can't + be set programmatically. + + Just look at what SDL's WinRT video backend code detected with regards + to fullscreen (being active, or not), and figure out a return/error code + from that. + */ + flags = window->flags; +#endif + if (title) { SDL_SetWindowTitle(window, title); } @@ -4382,6 +4425,9 @@ SDL_GetMessageBoxCount(void) #if SDL_VIDEO_DRIVER_WINDOWS #include "windows/SDL_windowsmessagebox.h" #endif +#if SDL_VIDEO_DRIVER_WINRT +#include "winrt/SDL_winrtmessagebox.h" +#endif #if SDL_VIDEO_DRIVER_COCOA #include "cocoa/SDL_cocoamessagebox.h" #endif @@ -4404,7 +4450,7 @@ SDL_GetMessageBoxCount(void) #include "vita/SDL_vitamessagebox.h" #endif -#if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_COCOA || SDL_VIDEO_DRIVER_UIKIT || SDL_VIDEO_DRIVER_X11 || SDL_VIDEO_DRIVER_WAYLAND || SDL_VIDEO_DRIVER_HAIKU || SDL_VIDEO_DRIVER_RISCOS +#if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT || SDL_VIDEO_DRIVER_COCOA || SDL_VIDEO_DRIVER_UIKIT || SDL_VIDEO_DRIVER_X11 || SDL_VIDEO_DRIVER_WAYLAND || SDL_VIDEO_DRIVER_HAIKU || SDL_VIDEO_DRIVER_RISCOS static SDL_bool SDL_MessageboxValidForDriver(const SDL_MessageBoxData *messageboxdata, SDL_SYSWM_TYPE drivertype) { SDL_SysWMinfo info; @@ -4477,6 +4523,13 @@ SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid) retval = 0; } #endif +#if SDL_VIDEO_DRIVER_WINRT + if (retval == -1 && + SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_WINRT) && + WINRT_ShowMessageBox(messageboxdata, buttonid) == 0) { + retval = 0; + } +#endif #if SDL_VIDEO_DRIVER_COCOA if (retval == -1 && SDL_MessageboxValidForDriver(messageboxdata, SDL_SYSWM_COCOA) && diff --git a/src/video/winrt/SDL_winrtevents.cpp b/src/video/winrt/SDL_winrtevents.cpp new file mode 100644 index 000000000..646bde773 --- /dev/null +++ b/src/video/winrt/SDL_winrtevents.cpp @@ -0,0 +1,153 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_internal.h" + +#if SDL_VIDEO_DRIVER_WINRT + +/* + * Windows includes: + */ +#include +using namespace Windows::UI::Core; +using Windows::UI::Core::CoreCursor; + +/* + * SDL includes: + */ +#include "SDL_winrtevents_c.h" +#include "../../core/winrt/SDL_winrtapp_common.h" +#include "../../core/winrt/SDL_winrtapp_direct3d.h" +#include "../../core/winrt/SDL_winrtapp_xaml.h" +#include "SDL_system.h" + +extern "C" { +#include "../../thread/SDL_systhread.h" +#include "../SDL_sysvideo.h" +#include "../../events/SDL_events_c.h" +} + + +/* Forward declarations */ +static void WINRT_YieldXAMLThread(); + + +/* Global event management */ + +void +WINRT_PumpEvents(_THIS) +{ + if (SDL_WinRTGlobalApp) { + SDL_WinRTGlobalApp->PumpEvents(); + } else if (WINRT_XAMLWasEnabled) { + WINRT_YieldXAMLThread(); + } +} + + +/* XAML Thread management */ + +enum SDL_XAMLAppThreadState +{ + ThreadState_NotLaunched = 0, + ThreadState_Running, + ThreadState_Yielding +}; + +static SDL_XAMLAppThreadState _threadState = ThreadState_NotLaunched; +static SDL_Thread * _XAMLThread = nullptr; +static SDL_mutex * _mutex = nullptr; +static SDL_cond * _cond = nullptr; + +static void +WINRT_YieldXAMLThread() +{ + SDL_LockMutex(_mutex); + SDL_assert(_threadState == ThreadState_Running); + _threadState = ThreadState_Yielding; + SDL_UnlockMutex(_mutex); + + SDL_CondSignal(_cond); + + SDL_LockMutex(_mutex); + while (_threadState != ThreadState_Running) { + SDL_CondWait(_cond, _mutex); + } + SDL_UnlockMutex(_mutex); +} + +static int +WINRT_XAMLThreadMain(void * userdata) +{ + // TODO, WinRT: pass the C-style main() a reasonably realistic + // representation of command line arguments. + int argc = 0; + char **argv = NULL; + return WINRT_SDLAppEntryPoint(argc, argv); +} + +void +WINRT_CycleXAMLThread(void) +{ + switch (_threadState) { + case ThreadState_NotLaunched: + { + _cond = SDL_CreateCond(); + + _mutex = SDL_CreateMutex(); + _threadState = ThreadState_Running; + _XAMLThread = SDL_CreateThreadInternal(WINRT_XAMLThreadMain, "SDL/XAML App Thread", 0, nullptr); + + SDL_LockMutex(_mutex); + while (_threadState != ThreadState_Yielding) { + SDL_CondWait(_cond, _mutex); + } + SDL_UnlockMutex(_mutex); + + break; + } + + case ThreadState_Running: + { + SDL_assert(false); + break; + } + + case ThreadState_Yielding: + { + SDL_LockMutex(_mutex); + SDL_assert(_threadState == ThreadState_Yielding); + _threadState = ThreadState_Running; + SDL_UnlockMutex(_mutex); + + SDL_CondSignal(_cond); + + SDL_LockMutex(_mutex); + while (_threadState != ThreadState_Yielding) { + SDL_CondWait(_cond, _mutex); + } + SDL_UnlockMutex(_mutex); + } + } +} + +#endif /* SDL_VIDEO_DRIVER_WINRT */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/winrt/SDL_winrtevents_c.h b/src/video/winrt/SDL_winrtevents_c.h new file mode 100644 index 000000000..67c18b865 --- /dev/null +++ b/src/video/winrt/SDL_winrtevents_c.h @@ -0,0 +1,83 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_sysvideo.h" +} + +/* + * Internal-use, C-style functions: + */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern void WINRT_InitTouch(_THIS); +extern void WINRT_PumpEvents(_THIS); + +#ifdef __cplusplus +} +#endif + + +/* + * Internal-use, C++/CX functions: + */ +#ifdef __cplusplus_winrt + +/* Pointers (Mice, Touch, etc.) */ +typedef enum { + NormalizeZeroToOne, + TransformToSDLWindowSize +} WINRT_CursorNormalizationType; +extern Windows::Foundation::Point WINRT_TransformCursorPosition(SDL_Window * window, + Windows::Foundation::Point rawPosition, + WINRT_CursorNormalizationType normalization); +extern SDL_bool WINRT_GetSDLButtonForPointerPoint(Windows::UI::Input::PointerPoint ^pt, Uint8 *button, Uint8 *pressed); +extern void WINRT_ProcessPointerPressedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint); +extern void WINRT_ProcessPointerMovedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint); +extern void WINRT_ProcessPointerReleasedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint); +extern void WINRT_ProcessPointerEnteredEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint); +extern void WINRT_ProcessPointerExitedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint); +extern void WINRT_ProcessPointerWheelChangedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint); +extern void WINRT_ProcessMouseMovedEvent(SDL_Window * window, Windows::Devices::Input::MouseEventArgs ^args); + +/* Keyboard */ +extern void WINRT_ProcessKeyDownEvent(Windows::UI::Core::KeyEventArgs ^args); +extern void WINRT_ProcessKeyUpEvent(Windows::UI::Core::KeyEventArgs ^args); +extern void WINRT_ProcessCharacterReceivedEvent(Windows::UI::Core::CharacterReceivedEventArgs ^args); + +#if NTDDI_VERSION >= NTDDI_WIN10 +extern void WINTRT_InitialiseInputPaneEvents(_THIS); +extern SDL_bool WINRT_HasScreenKeyboardSupport(_THIS); +extern void WINRT_ShowScreenKeyboard(_THIS, SDL_Window *window); +extern void WINRT_HideScreenKeyboard(_THIS, SDL_Window *window); +extern SDL_bool WINRT_IsScreenKeyboardShown(_THIS, SDL_Window *window); +#endif // NTDDI_VERSION >= ... + +/* XAML Thread Management */ +extern void WINRT_CycleXAMLThread(void); + +#endif // ifdef __cplusplus_winrt + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/winrt/SDL_winrtgamebar.cpp b/src/video/winrt/SDL_winrtgamebar.cpp new file mode 100644 index 000000000..2b1235721 --- /dev/null +++ b/src/video/winrt/SDL_winrtgamebar.cpp @@ -0,0 +1,196 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_internal.h" + +#if SDL_VIDEO_DRIVER_WINRT + +/* Windows includes */ +#include +#include +#include + + +/* SDL includes */ +extern "C" { +#include "SDL_mouse.h" +#include "../SDL_sysvideo.h" +} +#include "SDL_winrtvideo_cpp.h" + + +/* Game Bar events can come in off the main thread. Use the following + WinRT CoreDispatcher to deal with them on SDL's thread. +*/ +static Platform::WeakReference WINRT_MainThreadDispatcher; + + +/* Win10's initial SDK (the 10.0.10240.0 release) does not include references + to Game Bar APIs, as the Game Bar was released via Win10 10.0.10586.0. + + Declare its WinRT/COM interface here, to allow compilation with earlier + Windows SDKs. +*/ +MIDL_INTERFACE("1DB9A292-CC78-4173-BE45-B61E67283EA7") +IGameBarStatics_ : public IInspectable +{ +public: + virtual HRESULT STDMETHODCALLTYPE add_VisibilityChanged( + __FIEventHandler_1_IInspectable *handler, + Windows::Foundation::EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_VisibilityChanged( + Windows::Foundation::EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_IsInputRedirectedChanged( + __FIEventHandler_1_IInspectable *handler, + Windows::Foundation::EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_IsInputRedirectedChanged( + Windows::Foundation::EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Visible( + boolean *value) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_IsInputRedirected( + boolean *value) = 0; +}; + +/* Declare the game bar's COM GUID */ +static GUID IID_IGameBarStatics_ = { MAKELONG(0xA292, 0x1DB9), 0xCC78, 0x4173, { 0xBE, 0x45, 0xB6, 0x1E, 0x67, 0x28, 0x3E, 0xA7 } }; + +/* Retrieves a pointer to the game bar, or NULL if it is not available. + If a pointer is returned, it's ->Release() method must be called + after the caller has finished using it. +*/ +static IGameBarStatics_ * +WINRT_GetGameBar() +{ + wchar_t *wClassName = L"Windows.Gaming.UI.GameBar"; + HSTRING hClassName; + IActivationFactory *pActivationFactory = NULL; + IGameBarStatics_ *pGameBar = NULL; + HRESULT hr; + + hr = ::WindowsCreateString(wClassName, (UINT32)SDL_wcslen(wClassName), &hClassName); + if (FAILED(hr)) { + goto done; + } + + hr = Windows::Foundation::GetActivationFactory(hClassName, &pActivationFactory); + if (FAILED(hr)) { + goto done; + } + + pActivationFactory->QueryInterface(IID_IGameBarStatics_, (void **) &pGameBar); + +done: + if (pActivationFactory) { + pActivationFactory->Release(); + } + if (hClassName) { + ::WindowsDeleteString(hClassName); + } + return pGameBar; +} + +static void +WINRT_HandleGameBarIsInputRedirected_MainThread() +{ + IGameBarStatics_ *gameBar; + boolean isInputRedirected = 0; + if (!WINRT_MainThreadDispatcher) { + /* The game bar event handler has been deregistered! */ + return; + } + gameBar = WINRT_GetGameBar(); + if (!gameBar) { + /* Shouldn't happen, but just in case... */ + return; + } + if (SUCCEEDED(gameBar->get_IsInputRedirected(&isInputRedirected))) { + if ( ! isInputRedirected) { + /* Input-control is now back to the SDL app. Restore the cursor, + in case Windows does not (it does not in either Win10 + 10.0.10240.0 or 10.0.10586.0, maybe later version(s) too. + */ + SDL_Cursor *cursor = SDL_GetCursor(); + SDL_SetCursor(cursor); + } + } + gameBar->Release(); +} + +static void +WINRT_HandleGameBarIsInputRedirected_NonMainThread(Platform::Object ^ o1, Platform::Object ^o2) +{ + Windows::UI::Core::CoreDispatcher ^dispatcher = WINRT_MainThreadDispatcher.Resolve(); + if (dispatcher) { + dispatcher->RunAsync( + Windows::UI::Core::CoreDispatcherPriority::Normal, + ref new Windows::UI::Core::DispatchedHandler(&WINRT_HandleGameBarIsInputRedirected_MainThread)); + } +} + +void +WINRT_InitGameBar(_THIS) +{ + SDL_VideoData *driverdata = (SDL_VideoData *)_this->driverdata; + IGameBarStatics_ *gameBar = WINRT_GetGameBar(); + if (gameBar) { + /* GameBar.IsInputRedirected events can come in via something other than + the main/SDL thread. + + Get a WinRT 'CoreDispatcher' that can be used to call back into the + SDL thread. + */ + WINRT_MainThreadDispatcher = Windows::UI::Core::CoreWindow::GetForCurrentThread()->Dispatcher; + Windows::Foundation::EventHandler ^handler = \ + ref new Windows::Foundation::EventHandler(&WINRT_HandleGameBarIsInputRedirected_NonMainThread); + __FIEventHandler_1_IInspectable * pHandler = reinterpret_cast<__FIEventHandler_1_IInspectable *>(handler); + gameBar->add_IsInputRedirectedChanged(pHandler, &driverdata->gameBarIsInputRedirectedToken); + gameBar->Release(); + } +} + +void +WINRT_QuitGameBar(_THIS) +{ + SDL_VideoData *driverdata; + IGameBarStatics_ *gameBar; + if (!_this || !_this->driverdata) { + return; + } + gameBar = WINRT_GetGameBar(); + if (!gameBar) { + return; + } + driverdata = (SDL_VideoData *)_this->driverdata; + if (driverdata->gameBarIsInputRedirectedToken.Value) { + gameBar->remove_IsInputRedirectedChanged(driverdata->gameBarIsInputRedirectedToken); + driverdata->gameBarIsInputRedirectedToken.Value = 0; + } + WINRT_MainThreadDispatcher = nullptr; + gameBar->Release(); +} + +#endif /* SDL_VIDEO_DRIVER_WINRT */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/winrt/SDL_winrtgamebar_cpp.h b/src/video/winrt/SDL_winrtgamebar_cpp.h new file mode 100644 index 000000000..a77758791 --- /dev/null +++ b/src/video/winrt/SDL_winrtgamebar_cpp.h @@ -0,0 +1,35 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_winrtgamebar_h_ +#define SDL_winrtgamebar_h_ + +#ifdef __cplusplus +/* These are exported as C++ functions, rather than C, to fix a compilation + bug with MSVC 2013, for Windows 8.x builds. */ +extern void WINRT_InitGameBar(_THIS); +extern void WINRT_QuitGameBar(_THIS); +#endif + +#endif /* SDL_winrtgamebar_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/winrt/SDL_winrtkeyboard.cpp b/src/video/winrt/SDL_winrtkeyboard.cpp new file mode 100644 index 000000000..f6ff0a409 --- /dev/null +++ b/src/video/winrt/SDL_winrtkeyboard.cpp @@ -0,0 +1,466 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_internal.h" + +#if SDL_VIDEO_DRIVER_WINRT + +/* Windows-specific includes */ +#include +#include + + +/* SDL-specific includes */ +#include "SDL.h" +#include "SDL_winrtevents_c.h" + +extern "C" { +#include "../../events/scancodes_windows.h" +#include "../../events/SDL_keyboard_c.h" +} + + +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 */ + SDL_SCANCODE_AC_BACK, /* VirtualKey.GoBack -- 166 : The go back key. */ + SDL_SCANCODE_AC_FORWARD, /* VirtualKey.GoForward -- 167 : The go forward key. */ + SDL_SCANCODE_AC_REFRESH, /* VirtualKey.Refresh -- 168 : The refresh key. */ + SDL_SCANCODE_AC_STOP, /* VirtualKey.Stop -- 169 : The stop key. */ + SDL_SCANCODE_AC_SEARCH, /* VirtualKey.Search -- 170 : The search key. */ + SDL_SCANCODE_AC_BOOKMARKS, /* VirtualKey.Favorites -- 171 : The favorites key. */ + SDL_SCANCODE_AC_HOME /* VirtualKey.GoHome -- 172 : The go home key. */ +}; + +/* Attempt to translate a keycode that isn't listed in WinRT's VirtualKey enum. + */ +static SDL_Scancode +WINRT_TranslateUnofficialKeycode(int keycode) +{ + switch (keycode) { + case 173: return SDL_SCANCODE_MUTE; /* VK_VOLUME_MUTE */ + case 174: return SDL_SCANCODE_VOLUMEDOWN; /* VK_VOLUME_DOWN */ + case 175: return SDL_SCANCODE_VOLUMEUP; /* VK_VOLUME_UP */ + case 176: return SDL_SCANCODE_AUDIONEXT; /* VK_MEDIA_NEXT_TRACK */ + case 177: return SDL_SCANCODE_AUDIOPREV; /* VK_MEDIA_PREV_TRACK */ + // case 178: return ; /* VK_MEDIA_STOP */ + case 179: return SDL_SCANCODE_AUDIOPLAY; /* VK_MEDIA_PLAY_PAUSE */ + case 180: return SDL_SCANCODE_MAIL; /* VK_LAUNCH_MAIL */ + case 181: return SDL_SCANCODE_MEDIASELECT; /* VK_LAUNCH_MEDIA_SELECT */ + // case 182: return ; /* VK_LAUNCH_APP1 */ + case 183: return SDL_SCANCODE_CALCULATOR; /* VK_LAUNCH_APP2 */ + // case 184: return ; /* ... reserved ... */ + // case 185: return ; /* ... reserved ... */ + case 186: return SDL_SCANCODE_SEMICOLON; /* VK_OEM_1, ';:' key on US standard keyboards */ + case 187: return SDL_SCANCODE_EQUALS; /* VK_OEM_PLUS */ + case 188: return SDL_SCANCODE_COMMA; /* VK_OEM_COMMA */ + case 189: return SDL_SCANCODE_MINUS; /* VK_OEM_MINUS */ + case 190: return SDL_SCANCODE_PERIOD; /* VK_OEM_PERIOD */ + case 191: return SDL_SCANCODE_SLASH; /* VK_OEM_2, '/?' key on US standard keyboards */ + case 192: return SDL_SCANCODE_GRAVE; /* VK_OEM_3, '`~' key on US standard keyboards */ + // ? + // ... reserved or unassigned ... + // ? + case 219: return SDL_SCANCODE_LEFTBRACKET; /* VK_OEM_4, '[{' key on US standard keyboards */ + case 220: return SDL_SCANCODE_BACKSLASH; /* VK_OEM_5, '\|' key on US standard keyboards */ + case 221: return SDL_SCANCODE_RIGHTBRACKET; /* VK_OEM_6, ']}' key on US standard keyboards */ + case 222: return SDL_SCANCODE_APOSTROPHE; /* VK_OEM_7, 'single/double quote' on US standard keyboards */ + default: break; + } + return SDL_SCANCODE_UNKNOWN; +} + +static SDL_Scancode +WINRT_TranslateKeycode(int keycode, unsigned int nativeScancode) +{ + // TODO, WinRT: try filling out the WinRT keycode table as much as possible, using the Win32 table for interpretation hints + + SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN; + + /* HACK ALERT: At least one VirtualKey constant (Shift) with a left/right + * designation might not get reported with its correct handedness, however + * its hardware scan code can fill in the gaps. If this is detected, + * use the hardware scan code to try telling if the left, or the right + * side's key was used. + * + * If Microsoft ever allows MapVirtualKey or MapVirtualKeyEx to be used + * in WinRT apps, or something similar to these (it doesn't appear to be, + * at least not for Windows [Phone] 8/8.1, as of Oct 24, 2014), then this + * hack might become deprecated, or obsolete. + */ + if (nativeScancode < SDL_arraysize(windows_scancode_table)) { + switch (keycode) { + case 16: // VirtualKey.Shift + switch (windows_scancode_table[nativeScancode]) { + case SDL_SCANCODE_LSHIFT: + case SDL_SCANCODE_RSHIFT: + return windows_scancode_table[nativeScancode]; + } + break; + + // Add others, as necessary. + // + // Unfortunately, this hack doesn't seem to work in determining + // handedness with Control keys. + + default: + break; + } + } + + /* Try to get a documented, WinRT, 'VirtualKey' next (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. + If that fails, attempt to fall back to a scancode-derived key. + */ + if (keycode < SDL_arraysize(WinRT_Official_Keycodes)) { + scancode = WinRT_Official_Keycodes[keycode]; + } + if (scancode == SDL_SCANCODE_UNKNOWN) { + scancode = WINRT_TranslateUnofficialKeycode(keycode); + } + if (scancode == SDL_SCANCODE_UNKNOWN) { + if (nativeScancode < SDL_arraysize(windows_scancode_table)) { + scancode = windows_scancode_table[nativeScancode]; + } + } + /* + if (scancode == SDL_SCANCODE_UNKNOWN) { + SDL_Log("WinRT TranslateKeycode, unknown keycode=%d\n", (int)keycode); + } + */ + return scancode; +} + +void +WINRT_ProcessKeyDownEvent(Windows::UI::Core::KeyEventArgs ^args) +{ + SDL_Scancode sdlScancode = WINRT_TranslateKeycode((int)args->VirtualKey, args->KeyStatus.ScanCode); +#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=0x%x, 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_PRESSED, sdlScancode); +} + +void +WINRT_ProcessKeyUpEvent(Windows::UI::Core::KeyEventArgs ^args) +{ + SDL_Scancode sdlScancode = WINRT_TranslateKeycode((int)args->VirtualKey, args->KeyStatus.ScanCode); +#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=0x%x, 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 +WINRT_ProcessCharacterReceivedEvent(Windows::UI::Core::CharacterReceivedEventArgs ^args) +{ + wchar_t src_ucs2[2]; + char dest_utf8[16]; + int result; + + /* Setup src */ + src_ucs2[0] = args->KeyCode; + src_ucs2[1] = L'\0'; + + /* Convert the text, then send an SDL_TEXTINPUT event. */ + result = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)&src_ucs2, -1, (LPSTR)dest_utf8, sizeof(dest_utf8), NULL, NULL); + if (result > 0) { + SDL_SendKeyboardText(dest_utf8); + } +} + + +#if NTDDI_VERSION >= NTDDI_WIN10 + +static bool WINRT_InputPaneVisible = false; + +void WINTRT_OnInputPaneShowing(Windows::UI::ViewManagement::InputPane ^ sender, Windows::UI::ViewManagement::InputPaneVisibilityEventArgs ^ args) +{ + WINRT_InputPaneVisible = true; +} + +void WINTRT_OnInputPaneHiding(Windows::UI::ViewManagement::InputPane ^ sender, Windows::UI::ViewManagement::InputPaneVisibilityEventArgs ^ args) +{ + WINRT_InputPaneVisible = false; +} + +void WINTRT_InitialiseInputPaneEvents(_THIS) +{ + using namespace Windows::UI::ViewManagement; + InputPane ^ inputPane = InputPane::GetForCurrentView(); + if (inputPane) { + inputPane->Showing += ref new Windows::Foundation::TypedEventHandler(&WINTRT_OnInputPaneShowing); + inputPane->Hiding += ref new Windows::Foundation::TypedEventHandler(&WINTRT_OnInputPaneHiding); + } +} + +SDL_bool WINRT_HasScreenKeyboardSupport(_THIS) +{ + return SDL_TRUE; +} + +void WINRT_ShowScreenKeyboard(_THIS, SDL_Window *window) +{ + using namespace Windows::UI::ViewManagement; + InputPane ^ inputPane = InputPane::GetForCurrentView(); + if (inputPane) { + inputPane->TryShow(); + } +} + +void WINRT_HideScreenKeyboard(_THIS, SDL_Window *window) +{ + using namespace Windows::UI::ViewManagement; + InputPane ^ inputPane = InputPane::GetForCurrentView(); + if (inputPane) { + inputPane->TryHide(); + } +} + +SDL_bool WINRT_IsScreenKeyboardShown(_THIS, SDL_Window *window) +{ + using namespace Windows::UI::ViewManagement; + InputPane ^ inputPane = InputPane::GetForCurrentView(); + if (inputPane) { + switch (SDL_WinRTGetDeviceFamily()) { + case SDL_WINRT_DEVICEFAMILY_XBOX: + //Documentation recommends using inputPane->Visible + //https://learn.microsoft.com/en-us/uwp/api/windows.ui.viewmanagement.inputpane.visible?view=winrt-22621 + //This does not seem to work on latest UWP/Xbox. + //Workaround: Listen to Showing/Hiding events + if (WINRT_InputPaneVisible) { + return SDL_TRUE; + } + break; + default: + //OccludedRect is recommend on universal apps per docs + //https://learn.microsoft.com/en-us/uwp/api/windows.ui.viewmanagement.inputpane.visible?view=winrt-22621 + Windows::Foundation::Rect rect = inputPane->OccludedRect; + if (rect.Width > 0 && rect.Height > 0) { + return SDL_TRUE; + } + break; + } + } + return SDL_FALSE; +} + +#endif // NTDDI_VERSION >= ... + +#endif // SDL_VIDEO_DRIVER_WINRT \ No newline at end of file diff --git a/src/video/winrt/SDL_winrtmessagebox.cpp b/src/video/winrt/SDL_winrtmessagebox.cpp new file mode 100644 index 000000000..f74a7f483 --- /dev/null +++ b/src/video/winrt/SDL_winrtmessagebox.cpp @@ -0,0 +1,118 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_internal.h" + +#if SDL_VIDEO_DRIVER_WINRT + +extern "C" { +#include "SDL_messagebox.h" +#include "../../core/windows/SDL_windows.h" +} + +#include "SDL_winrtevents_c.h" + +#include +using namespace Platform; +using namespace Windows::Foundation; +using namespace Windows::UI::Popups; + +static String ^ +WINRT_UTF8ToPlatformString(const char * str) +{ + wchar_t * wstr = WIN_UTF8ToString(str); + String ^ rtstr = ref new String(wstr); + SDL_free(wstr); + return rtstr; +} + +extern "C" int +WINRT_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid) +{ +#if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) && (NTDDI_VERSION == NTDDI_WIN8) + /* Sadly, Windows Phone 8 doesn't include the MessageDialog class that + * Windows 8.x/RT does, even though MSDN's reference documentation for + * Windows Phone 8 mentions it. + * + * The .NET runtime on Windows Phone 8 does, however, include a + * MessageBox class. Perhaps this could be called, somehow? + */ + return SDL_SetError("SDL_messagebox support is not available for Windows Phone 8.0"); +#else + SDL_VideoDevice *_this = SDL_GetVideoDevice(); + +#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP + const int maxbuttons = 2; + const char * platform = "Windows Phone 8.1+"; +#else + const int maxbuttons = 3; + const char * platform = "Windows 8.x"; +#endif + + if (messageboxdata->numbuttons > maxbuttons) { + return SDL_SetError("WinRT's MessageDialog only supports %d buttons, at most, on %s. %d were requested.", + maxbuttons, platform, messageboxdata->numbuttons); + } + + /* Build a MessageDialog object and its buttons */ + MessageDialog ^ dialog = ref new MessageDialog(WINRT_UTF8ToPlatformString(messageboxdata->message)); + dialog->Title = WINRT_UTF8ToPlatformString(messageboxdata->title); + for (int i = 0; i < messageboxdata->numbuttons; ++i) { + const SDL_MessageBoxButtonData *sdlButton; + if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) { + sdlButton = &messageboxdata->buttons[messageboxdata->numbuttons - 1 - i]; + } else { + sdlButton = &messageboxdata->buttons[i]; + } + UICommand ^ button = ref new UICommand(WINRT_UTF8ToPlatformString(sdlButton->text)); + button->Id = IntPtr((int)((size_t)(sdlButton - messageboxdata->buttons))); + dialog->Commands->Append(button); + if (sdlButton->flags & SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT) { + dialog->CancelCommandIndex = i; + } + if (sdlButton->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) { + dialog->DefaultCommandIndex = i; + } + } + + /* Display the MessageDialog, then wait for it to be closed */ + /* TODO, WinRT: Find a way to redraw MessageDialog instances if a GPU device-reset occurs during the following event-loop */ + auto operation = dialog->ShowAsync(); + while (operation->Status == Windows::Foundation::AsyncStatus::Started) { + WINRT_PumpEvents(_this); + } + + /* Retrieve results from the MessageDialog and process them accordingly */ + if (operation->Status != Windows::Foundation::AsyncStatus::Completed) { + return SDL_SetError("An unknown error occurred in displaying the WinRT MessageDialog"); + } + if (buttonid) { + IntPtr results = safe_cast(operation->GetResults()->Id); + int clicked_index = results.ToInt32(); + *buttonid = messageboxdata->buttons[clicked_index].buttonid; + } + return 0; +#endif /* if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP / else */ +} + +#endif /* SDL_VIDEO_DRIVER_WINRT */ + +/* vi: set ts=4 sw=4 expandtab: */ + diff --git a/src/video/winrt/SDL_winrtmessagebox.h b/src/video/winrt/SDL_winrtmessagebox.h new file mode 100644 index 000000000..7d1429dbc --- /dev/null +++ b/src/video/winrt/SDL_winrtmessagebox.h @@ -0,0 +1,29 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_internal.h" + +#if SDL_VIDEO_DRIVER_WINRT + +extern int WINRT_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid); + +#endif /* SDL_VIDEO_DRIVER_WINRT */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/winrt/SDL_winrtmouse.cpp b/src/video/winrt/SDL_winrtmouse.cpp new file mode 100644 index 000000000..24e44682e --- /dev/null +++ b/src/video/winrt/SDL_winrtmouse.cpp @@ -0,0 +1,222 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_internal.h" + +#if SDL_VIDEO_DRIVER_WINRT + +/* + * Windows includes: + */ +#include +#include +using namespace Windows::UI::Core; +using Windows::UI::Core::CoreCursor; + +/* + * SDL includes: + */ +extern "C" { +#include "../../events/SDL_mouse_c.h" +#include "../../events/SDL_touch_c.h" +#include "../SDL_sysvideo.h" +#include "SDL_events.h" +} + +#include "../../core/winrt/SDL_winrtapp_direct3d.h" +#include "SDL_winrtvideo_cpp.h" +#include "SDL_winrtmouse_c.h" + + +extern "C" SDL_bool WINRT_UsingRelativeMouseMode = SDL_FALSE; + + +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) +{ + // TODO, WinRT, XAML: make WINRT_ShowCursor work when XAML support is enabled. + if ( ! CoreWindow::GetForCurrentThread()) { + return 0; + } + + CoreWindow ^ coreWindow = CoreWindow::GetForCurrentThread(); + if (cursor) { + CoreCursor ^* theCursor = (CoreCursor ^*) cursor->driverdata; + coreWindow->PointerCursor = *theCursor; + } else { + // HACK ALERT: TL;DR - Hiding the cursor in WinRT/UWP apps is weird, and + // a Win32-style cursor resource file must be directly included in apps, + // otherwise hiding the cursor will cause mouse-motion data to never be + // received. + // + // Here's the lengthy explanation: + // + // There are two ways to hide a cursor in WinRT/UWP apps. + // Both involve setting the WinRT CoreWindow's (which is somewhat analogous + // to a Win32 HWND) 'PointerCursor' property. + // + // The first way to hide a cursor sets PointerCursor to nullptr. This + // is, arguably, the easiest to implement for an app. It does have an + // unfortunate side-effect: it'll prevent mouse-motion events from being + // sent to the app (via CoreWindow). + // + // The second way to hide a cursor sets PointerCursor to a transparent + // cursor. This allows mouse-motion events to be sent to the app, but is + // more difficult to set up, as: + // 1. WinRT/UWP, while providing a few stock cursors, does not provide + // a completely transparent cursor. + // 2. WinRT/UWP allows apps to provide custom-built cursors, but *ONLY* + // if they are linked directly inside the app, via Win32-style + // cursor resource files. APIs to create cursors at runtime are + // not provided to apps, and attempting to link-to or use Win32 + // cursor-creation APIs could cause an app to fail Windows Store + // certification. + // + // SDL can use either means of hiding the cursor. It provides a Win32-style + // set of cursor resource files in its source distribution, inside + // src/main/winrt/. If those files are linked to an SDL-for-WinRT/UWP app + // (by including them in a MSVC project, for example), SDL will attempt to + // use those, if and when the cursor is hidden via SDL APIs. If those + // files are not linked in, SDL will attempt to hide the cursor via the + // 'set PointerCursor to nullptr' means (which, if you recall, causes + // mouse-motion data to NOT be sent to the app!). + // + // Tech notes: + // - SDL's blank cursor resource uses a resource ID of 5000. + // - SDL's cursor resources consist of the following two files: + // - src/main/winrt/SDL3-WinRTResource_BlankCursor.cur -- cursor pixel data + // - src/main/winrt/SDL3-WinRTResources.rc -- declares the cursor resource, and its ID (of 5000) + // + + const unsigned int win32CursorResourceID = 5000; + CoreCursor ^ blankCursor = ref new CoreCursor(CoreCursorType::Custom, win32CursorResourceID); + + // Set 'PointerCursor' to 'blankCursor' in a way that shouldn't throw + // an exception if the app hasn't loaded that resource. + ABI::Windows::UI::Core::ICoreCursor * iblankCursor = reinterpret_cast(blankCursor); + ABI::Windows::UI::Core::ICoreWindow * icoreWindow = reinterpret_cast(coreWindow); + HRESULT hr = icoreWindow->put_PointerCursor(iblankCursor); + if (FAILED(hr)) { + // The app doesn't contain the cursor resource, or some other error + // occurred. Just use the other, but mouse-motion-preventing, means of + // hiding the cursor. + coreWindow->PointerCursor = nullptr; + } + } + return 0; +} + +static int +WINRT_SetRelativeMouseMode(SDL_bool enabled) +{ + WINRT_UsingRelativeMouseMode = enabled; + return 0; +} + +void +WINRT_InitMouse(_THIS) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + /* DLudwig, Dec 3, 2012: WinRT 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/winrt/SDL_winrtmouse_c.h b/src/video/winrt/SDL_winrtmouse_c.h new file mode 100644 index 000000000..f5008962a --- /dev/null +++ b/src/video/winrt/SDL_winrtmouse_c.h @@ -0,0 +1,40 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_winrtmouse_h_ +#define SDL_winrtmouse_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern void WINRT_InitMouse(_THIS); +extern void WINRT_QuitMouse(_THIS); +extern SDL_bool WINRT_UsingRelativeMouseMode; + +#ifdef __cplusplus +} +#endif + +#endif /* SDL_winrtmouse_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/winrt/SDL_winrtopengles.cpp b/src/video/winrt/SDL_winrtopengles.cpp new file mode 100644 index 000000000..d5301006e --- /dev/null +++ b/src/video/winrt/SDL_winrtopengles.cpp @@ -0,0 +1,203 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_internal.h" + +#if SDL_VIDEO_DRIVER_WINRT && SDL_VIDEO_OPENGL_EGL + +/* EGL implementation of SDL OpenGL support */ + +#include "SDL_winrtvideo_cpp.h" +extern "C" { +#include "SDL_winrtopengles.h" +#include "SDL_loadso.h" +#include "../SDL_egl_c.h" +} + +/* Windows includes */ +#include +using namespace Windows::UI::Core; + +/* ANGLE/WinRT constants */ +static const int ANGLE_D3D_FEATURE_LEVEL_ANY = 0; +#define EGL_PLATFORM_ANGLE_ANGLE 0x3202 +#define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3203 +#define EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE 0x3204 +#define EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE 0x3205 +#define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3208 +#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE 0x3209 +#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE 0x320B +#define EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE 0x320F + +#define EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER 0x320B + + +/* + * SDL/EGL top-level implementation + */ + +extern "C" int +WINRT_GLES_LoadLibrary(_THIS, const char *path) +{ + SDL_VideoData *video_data = (SDL_VideoData *)_this->driverdata; + + if (SDL_EGL_LoadLibrary(_this, path, EGL_DEFAULT_DISPLAY, 0) != 0) { + return -1; + } + + /* Load ANGLE/WinRT-specific functions */ + CreateWinrtEglWindow_Old_Function CreateWinrtEglWindow = (CreateWinrtEglWindow_Old_Function) SDL_LoadFunction(_this->egl_data->opengl_dll_handle, "CreateWinrtEglWindow"); + if (CreateWinrtEglWindow) { + /* 'CreateWinrtEglWindow' was found, which means that an an older + * version of ANGLE/WinRT is being used. Continue setting up EGL, + * as appropriate to this version of ANGLE. + */ + + /* Create an ANGLE/WinRT EGL-window */ + /* TODO, WinRT: check for XAML usage before accessing the CoreWindow, as not doing so could lead to a crash */ + CoreWindow ^ native_win = CoreWindow::GetForCurrentThread(); + Microsoft::WRL::ComPtr cpp_win = reinterpret_cast(native_win); + HRESULT result = CreateWinrtEglWindow(cpp_win, ANGLE_D3D_FEATURE_LEVEL_ANY, &(video_data->winrtEglWindow)); + if (FAILED(result)) { + return -1; + } + + /* Call eglGetDisplay and eglInitialize as appropriate. On other + * platforms, this would probably get done by SDL_EGL_LoadLibrary, + * however ANGLE/WinRT's current implementation (as of Mar 22, 2014) of + * eglGetDisplay requires that a C++ object be passed into it, so the + * call will be made in this file, a C++ file, instead. + */ + Microsoft::WRL::ComPtr cpp_display = video_data->winrtEglWindow; + _this->egl_data->egl_display = ((eglGetDisplay_Old_Function)_this->egl_data->eglGetDisplay)(cpp_display); + if (!_this->egl_data->egl_display) { + return SDL_EGL_SetError("Could not get Windows 8.0 EGL display", "eglGetDisplay"); + } + + if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) { + return SDL_EGL_SetError("Could not initialize Windows 8.0 EGL", "eglInitialize"); + } + } else { + /* Declare some ANGLE/EGL initialization property-sets, as suggested by + * MSOpenTech's ANGLE-for-WinRT template apps: + */ + const EGLint defaultDisplayAttributes[] = + { + EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, + EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, EGL_TRUE, + EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE, + EGL_NONE, + }; + + const EGLint fl9_3DisplayAttributes[] = + { + EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, + EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, 9, + EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, 3, + EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, EGL_TRUE, + EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE, + EGL_NONE, + }; + + const EGLint warpDisplayAttributes[] = + { + EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, + EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE, + EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, EGL_TRUE, + EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE, + EGL_NONE, + }; + + /* 'CreateWinrtEglWindow' was NOT found, which either means that a + * newer version of ANGLE/WinRT is being used, or that we don't have + * a valid copy of ANGLE. + * + * Try loading ANGLE as if it were the newer version. + */ + eglGetPlatformDisplayEXT_Function eglGetPlatformDisplayEXT = (eglGetPlatformDisplayEXT_Function)_this->egl_data->eglGetProcAddress("eglGetPlatformDisplayEXT"); + if (!eglGetPlatformDisplayEXT) { + return SDL_EGL_SetError("Could not retrieve ANGLE/WinRT display function(s)", "eglGetProcAddress"); + } + +#if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP) + /* Try initializing EGL at D3D11 Feature Level 10_0+ (which is not + * supported on WinPhone 8.x. + */ + _this->egl_data->egl_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, defaultDisplayAttributes); + if (!_this->egl_data->egl_display) { + return SDL_EGL_SetError("Could not get EGL display for Direct3D 10_0+", "eglGetPlatformDisplayEXT"); + } + + if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) +#endif + { + /* Try initializing EGL at D3D11 Feature Level 9_3, in case the + * 10_0 init fails, or we're on Windows Phone (which only supports + * 9_3). + */ + _this->egl_data->egl_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, fl9_3DisplayAttributes); + if (!_this->egl_data->egl_display) { + return SDL_EGL_SetError("Could not get EGL display for Direct3D 9_3", "eglGetPlatformDisplayEXT"); + } + + if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) { + /* Try initializing EGL at D3D11 Feature Level 11_0 on WARP + * (a Windows-provided, software rasterizer) if all else fails. + */ + _this->egl_data->egl_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, warpDisplayAttributes); + if (!_this->egl_data->egl_display) { + return SDL_EGL_SetError("Could not get EGL display for Direct3D WARP", "eglGetPlatformDisplayEXT"); + } + + if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) { + return SDL_EGL_SetError("Could not initialize WinRT 8.x+ EGL", "eglInitialize"); + } + } + } + } + + return 0; +} + +extern "C" void +WINRT_GLES_UnloadLibrary(_THIS) +{ + SDL_VideoData *video_data = (SDL_VideoData *)_this->driverdata; + + /* Release SDL's own COM reference to the ANGLE/WinRT IWinrtEglWindow */ + if (video_data->winrtEglWindow) { + video_data->winrtEglWindow->Release(); + video_data->winrtEglWindow = nullptr; + } + + /* Perform the bulk of the unloading */ + SDL_EGL_UnloadLibrary(_this); +} + +extern "C" { +SDL_EGL_CreateContext_impl(WINRT) +SDL_EGL_SwapWindow_impl(WINRT) +SDL_EGL_MakeCurrent_impl(WINRT) +} + +#endif /* SDL_VIDEO_DRIVER_WINRT && SDL_VIDEO_OPENGL_EGL */ + +/* vi: set ts=4 sw=4 expandtab: */ + diff --git a/src/video/winrt/SDL_winrtopengles.h b/src/video/winrt/SDL_winrtopengles.h new file mode 100644 index 000000000..c702c8b69 --- /dev/null +++ b/src/video/winrt/SDL_winrtopengles.h @@ -0,0 +1,70 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_winrtopengles_h_ +#define SDL_winrtopengles_h_ + +#if SDL_VIDEO_DRIVER_WINRT && SDL_VIDEO_OPENGL_EGL + +#include "../SDL_sysvideo.h" +#include "../SDL_egl_c.h" + +/* OpenGLES functions */ +#define WINRT_GLES_GetAttribute SDL_EGL_GetAttribute +#define WINRT_GLES_GetProcAddress SDL_EGL_GetProcAddress +#define WINRT_GLES_SetSwapInterval SDL_EGL_SetSwapInterval +#define WINRT_GLES_GetSwapInterval SDL_EGL_GetSwapInterval +#define WINRT_GLES_DeleteContext SDL_EGL_DeleteContext + +extern int WINRT_GLES_LoadLibrary(_THIS, const char *path); +extern void WINRT_GLES_UnloadLibrary(_THIS); +extern SDL_GLContext WINRT_GLES_CreateContext(_THIS, SDL_Window * window); +extern int WINRT_GLES_SwapWindow(_THIS, SDL_Window * window); +extern int WINRT_GLES_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context); + + +#ifdef __cplusplus + +/* Typedefs for ANGLE/WinRT's C++-based native-display and native-window types, + * which are used when calling eglGetDisplay and eglCreateWindowSurface. + */ +typedef Microsoft::WRL::ComPtr WINRT_EGLNativeWindowType_Old; + +/* Function pointer typedefs for 'old' ANGLE/WinRT's functions, which may + * require that C++ objects be passed in: + */ +typedef EGLDisplay (EGLAPIENTRY *eglGetDisplay_Old_Function)(WINRT_EGLNativeWindowType_Old); +typedef EGLSurface (EGLAPIENTRY *eglCreateWindowSurface_Old_Function)(EGLDisplay, EGLConfig, WINRT_EGLNativeWindowType_Old, const EGLint *); +typedef HRESULT (EGLAPIENTRY *CreateWinrtEglWindow_Old_Function)(Microsoft::WRL::ComPtr, int, IUnknown ** result); + +#endif /* __cplusplus */ + +/* Function pointer typedefs for 'new' ANGLE/WinRT functions, which, unlike + * the old functions, do not require C++ support and work with plain C. + */ +typedef EGLDisplay (EGLAPIENTRY *eglGetPlatformDisplayEXT_Function)(EGLenum, void *, const EGLint *); + +#endif /* SDL_VIDEO_DRIVER_WINRT && SDL_VIDEO_OPENGL_EGL */ + +#endif /* SDL_winrtopengles_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/winrt/SDL_winrtpointerinput.cpp b/src/video/winrt/SDL_winrtpointerinput.cpp new file mode 100644 index 000000000..51cc93727 --- /dev/null +++ b/src/video/winrt/SDL_winrtpointerinput.cpp @@ -0,0 +1,407 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_internal.h" + +#if SDL_VIDEO_DRIVER_WINRT + +/* SDL includes */ +#include "SDL_winrtevents_c.h" +#include "SDL_winrtmouse_c.h" +#include "SDL_winrtvideo_cpp.h" +#include "SDL_system.h" + +extern "C" { +#include "../SDL_sysvideo.h" +#include "../../events/SDL_events_c.h" +#include "../../events/SDL_mouse_c.h" +#include "../../events/SDL_touch_c.h" +} + +/* File-specific globals: */ +static SDL_TouchID WINRT_TouchID = 1; + + +void +WINRT_InitTouch(_THIS) +{ + SDL_AddTouch(WINRT_TouchID, SDL_TOUCH_DEVICE_DIRECT, ""); +} + + +// +// Applies necessary geometric transformations to raw cursor positions: +// +Windows::Foundation::Point +WINRT_TransformCursorPosition(SDL_Window * window, + Windows::Foundation::Point rawPosition, + WINRT_CursorNormalizationType normalization) +{ + using namespace Windows::UI::Core; + using namespace Windows::Graphics::Display; + + if (!window) { + return rawPosition; + } + + SDL_WindowData * windowData = (SDL_WindowData *) window->driverdata; + if (windowData->coreWindow == nullptr) { + // For some reason, the window isn't associated with a CoreWindow. + // This might end up being the case as XAML support is extended. + // For now, if there's no CoreWindow attached to the SDL_Window, + // don't do any transforms. + + // TODO, WinRT: make sure touch input coordinate ranges are correct when using XAML support + return rawPosition; + } + + // The CoreWindow can only be accessed on certain thread(s). + SDL_assert(CoreWindow::GetForCurrentThread() != nullptr); + + CoreWindow ^ nativeWindow = windowData->coreWindow.Get(); + Windows::Foundation::Point outputPosition; + + // Compute coordinates normalized from 0..1. + // If the coordinates need to be sized to the SDL window, + // we'll do that after. +#if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP) || (NTDDI_VERSION > NTDDI_WIN8) + outputPosition.X = rawPosition.X / nativeWindow->Bounds.Width; + outputPosition.Y = rawPosition.Y / nativeWindow->Bounds.Height; +#else + switch (WINRT_DISPLAY_PROPERTY(CurrentOrientation)) + { + case DisplayOrientations::Portrait: + outputPosition.X = rawPosition.X / nativeWindow->Bounds.Width; + outputPosition.Y = rawPosition.Y / nativeWindow->Bounds.Height; + break; + case DisplayOrientations::PortraitFlipped: + outputPosition.X = 1.0f - (rawPosition.X / nativeWindow->Bounds.Width); + outputPosition.Y = 1.0f - (rawPosition.Y / nativeWindow->Bounds.Height); + break; + case DisplayOrientations::Landscape: + outputPosition.X = rawPosition.Y / nativeWindow->Bounds.Height; + outputPosition.Y = 1.0f - (rawPosition.X / nativeWindow->Bounds.Width); + break; + case DisplayOrientations::LandscapeFlipped: + outputPosition.X = 1.0f - (rawPosition.Y / nativeWindow->Bounds.Height); + outputPosition.Y = rawPosition.X / nativeWindow->Bounds.Width; + break; + default: + break; + } +#endif + + if (normalization == TransformToSDLWindowSize) { + outputPosition.X *= ((float32) window->w); + outputPosition.Y *= ((float32) window->h); + } + + return outputPosition; +} + +SDL_bool +WINRT_GetSDLButtonForPointerPoint(Windows::UI::Input::PointerPoint ^pt, Uint8 *button, Uint8 *pressed) +{ + using namespace Windows::UI::Input; + +#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP + *button = SDL_BUTTON_LEFT; + return SDL_TRUE; +#else + switch (pt->Properties->PointerUpdateKind) + { + case PointerUpdateKind::LeftButtonPressed: + case PointerUpdateKind::LeftButtonReleased: + *button = SDL_BUTTON_LEFT; + *pressed = (pt->Properties->PointerUpdateKind == PointerUpdateKind::LeftButtonPressed); + return SDL_TRUE; + + case PointerUpdateKind::RightButtonPressed: + case PointerUpdateKind::RightButtonReleased: + *button = SDL_BUTTON_RIGHT; + *pressed = (pt->Properties->PointerUpdateKind == PointerUpdateKind::RightButtonPressed); + return SDL_TRUE; + + case PointerUpdateKind::MiddleButtonPressed: + case PointerUpdateKind::MiddleButtonReleased: + *button = SDL_BUTTON_MIDDLE; + *pressed = (pt->Properties->PointerUpdateKind == PointerUpdateKind::MiddleButtonPressed); + return SDL_TRUE; + + case PointerUpdateKind::XButton1Pressed: + case PointerUpdateKind::XButton1Released: + *button = SDL_BUTTON_X1; + *pressed = (pt->Properties->PointerUpdateKind == PointerUpdateKind::XButton1Pressed); + return SDL_TRUE; + + case PointerUpdateKind::XButton2Pressed: + case PointerUpdateKind::XButton2Released: + *button = SDL_BUTTON_X2; + *pressed = (pt->Properties->PointerUpdateKind == PointerUpdateKind::XButton2Pressed); + return SDL_TRUE; + + default: + break; + } +#endif + + *button = 0; + *pressed = 0; + return SDL_FALSE; +} + +//const char * +//WINRT_ConvertPointerUpdateKindToString(Windows::UI::Input::PointerUpdateKind kind) +//{ +// using namespace Windows::UI::Input; +// +// 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 bool +WINRT_IsTouchEvent(Windows::UI::Input::PointerPoint ^pointerPoint) +{ +#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP + return true; +#else + using namespace Windows::Devices::Input; + switch (pointerPoint->PointerDevice->PointerDeviceType) { + case PointerDeviceType::Touch: + case PointerDeviceType::Pen: + return true; + default: + return false; + } +#endif +} + +void WINRT_ProcessPointerPressedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint) +{ + if (!window) { + return; + } + + if ( ! WINRT_IsTouchEvent(pointerPoint)) { + Uint8 button, pressed; + WINRT_GetSDLButtonForPointerPoint(pointerPoint, &button, &pressed); + SDL_assert(pressed == 1); + SDL_SendMouseButton(window, 0, SDL_PRESSED, button); + } else { + Windows::Foundation::Point normalizedPoint = WINRT_TransformCursorPosition(window, pointerPoint->Position, NormalizeZeroToOne); + Windows::Foundation::Point windowPoint = WINRT_TransformCursorPosition(window, pointerPoint->Position, TransformToSDLWindowSize); + + SDL_SendTouch( + WINRT_TouchID, + (SDL_FingerID) pointerPoint->PointerId, + window, + SDL_TRUE, + normalizedPoint.X, + normalizedPoint.Y, + pointerPoint->Properties->Pressure); + } +} + +void +WINRT_ProcessPointerMovedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint) +{ + if (!window || WINRT_UsingRelativeMouseMode) { + return; + } + + Windows::Foundation::Point normalizedPoint = WINRT_TransformCursorPosition(window, pointerPoint->Position, NormalizeZeroToOne); + Windows::Foundation::Point windowPoint = WINRT_TransformCursorPosition(window, pointerPoint->Position, TransformToSDLWindowSize); + + if ( ! WINRT_IsTouchEvent(pointerPoint)) { + /* For some odd reason Moved events are used for multiple mouse buttons */ + Uint8 button, pressed; + if (WINRT_GetSDLButtonForPointerPoint(pointerPoint, &button, &pressed)) { + SDL_SendMouseButton(window, 0, pressed, button); + } + + SDL_SendMouseMotion(window, 0, 0, (int)windowPoint.X, (int)windowPoint.Y); + } else { + SDL_SendTouchMotion( + WINRT_TouchID, + (SDL_FingerID) pointerPoint->PointerId, + window, + normalizedPoint.X, + normalizedPoint.Y, + pointerPoint->Properties->Pressure); + } +} + +void WINRT_ProcessPointerReleasedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint) +{ + if (!window) { + return; + } + + if (!WINRT_IsTouchEvent(pointerPoint)) { + Uint8 button, pressed; + WINRT_GetSDLButtonForPointerPoint(pointerPoint, &button, &pressed); + SDL_assert(pressed == 0); + SDL_SendMouseButton(window, 0, SDL_RELEASED, button); + } else { + Windows::Foundation::Point normalizedPoint = WINRT_TransformCursorPosition(window, pointerPoint->Position, NormalizeZeroToOne); + + SDL_SendTouch( + WINRT_TouchID, + (SDL_FingerID) pointerPoint->PointerId, + window, + SDL_FALSE, + normalizedPoint.X, + normalizedPoint.Y, + pointerPoint->Properties->Pressure); + } +} + +void WINRT_ProcessPointerEnteredEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint) +{ + if (!window) { + return; + } + + if (!WINRT_IsTouchEvent(pointerPoint)) { + SDL_SetMouseFocus(window); + } +} + +void WINRT_ProcessPointerExitedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint) +{ + if (!window) { + return; + } + + if (!WINRT_IsTouchEvent(pointerPoint)) { + SDL_SetMouseFocus(NULL); + } +} + +void +WINRT_ProcessPointerWheelChangedEvent(SDL_Window *window, Windows::UI::Input::PointerPoint ^pointerPoint) +{ + if (!window) { + return; + } + + float motion = (float) pointerPoint->Properties->MouseWheelDelta / WHEEL_DELTA; + SDL_SendMouseWheel(window, 0, 0, (float) motion, SDL_MOUSEWHEEL_NORMAL); +} + +void +WINRT_ProcessMouseMovedEvent(SDL_Window * window, Windows::Devices::Input::MouseEventArgs ^args) +{ + if (!window || !WINRT_UsingRelativeMouseMode) { + return; + } + + // 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 WinRT 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 Windows::Foundation::Point mouseDeltaInDIPs((float)args->MouseDelta.X, (float)args->MouseDelta.Y); + const Windows::Foundation::Point mouseDeltaInSDLWindowCoords = WINRT_TransformCursorPosition(window, mouseDeltaInDIPs, TransformToSDLWindowSize); + SDL_SendMouseMotion( + window, + 0, + 1, + SDL_lroundf(mouseDeltaInSDLWindowCoords.X), + SDL_lroundf(mouseDeltaInSDLWindowCoords.Y)); +} + +#endif // SDL_VIDEO_DRIVER_WINRT diff --git a/src/video/winrt/SDL_winrtvideo.cpp b/src/video/winrt/SDL_winrtvideo.cpp new file mode 100644 index 000000000..7511aa531 --- /dev/null +++ b/src/video/winrt/SDL_winrtvideo.cpp @@ -0,0 +1,907 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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_internal.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. + */ + +/* Standard C++11 includes */ +#include +#include +#include +using namespace std; + +/* Windows includes */ +#include +#include +#include +#include +#include +using namespace Windows::ApplicationModel::Core; +using namespace Windows::Foundation; +using namespace Windows::Graphics::Display; +using namespace Windows::UI::Core; +using namespace Windows::UI::ViewManagement; + + +/* [re]declare Windows GUIDs locally, to limit the amount of external lib(s) SDL has to link to */ +static const GUID SDL_IID_IDisplayRequest = { 0xe5732044, 0xf49f, 0x4b60, { 0x8d, 0xd4, 0x5e, 0x7e, 0x3a, 0x63, 0x2a, 0xc0 } }; +static const GUID SDL_IID_IDXGIFactory2 = { 0x50c83a1c, 0xe072, 0x4c48, { 0x87, 0xb0, 0x36, 0x30, 0xfa, 0x36, 0xa6, 0xd0 } }; + + +/* SDL includes */ +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_winrtopengles.h" +#include "../../core/windows/SDL_windows.h" +} + +#include "../../core/winrt/SDL_winrtapp_direct3d.h" +#include "../../core/winrt/SDL_winrtapp_xaml.h" +#include "SDL_winrtvideo_cpp.h" +#include "SDL_winrtevents_c.h" +#include "SDL_winrtgamebar_cpp.h" +#include "SDL_winrtmouse_c.h" +#include "SDL_main.h" +#include "SDL_system.h" +#include "SDL_hints.h" + + +/* 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_SetWindowSize(_THIS, SDL_Window * window); +static void WINRT_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen); +static void WINRT_DestroyWindow(_THIS, SDL_Window * window); +static SDL_bool WINRT_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info); + + +/* Misc functions */ +static ABI::Windows::System::Display::IDisplayRequest * WINRT_CreateDisplayRequest(_THIS); +extern void WINRT_SuspendScreenSaver(_THIS); + + +/* SDL-internal globals: */ +SDL_Window * WINRT_GlobalSDLWindow = NULL; + + +/* WinRT driver bootstrap functions */ + +static void +WINRT_DeleteDevice(SDL_VideoDevice * device) +{ + if (device->driverdata) { + SDL_VideoData * video_data = (SDL_VideoData *)device->driverdata; + if (video_data->winrtEglWindow) { + video_data->winrtEglWindow->Release(); + } + SDL_free(video_data); + } + + SDL_free(device); +} + +static SDL_VideoDevice * +WINRT_CreateDevice(void) +{ + SDL_VideoDevice *device; + SDL_VideoData *data; + + /* Initialize all variables that we clean on shutdown */ + device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice)); + if (!device) { + SDL_OutOfMemory(); + return (0); + } + + data = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData)); + if (!data) { + SDL_OutOfMemory(); + SDL_free(device); + return (0); + } + device->driverdata = data; + + /* Set the function pointers */ + device->VideoInit = WINRT_VideoInit; + device->VideoQuit = WINRT_VideoQuit; + device->CreateSDLWindow = WINRT_CreateWindow; + device->SetWindowSize = WINRT_SetWindowSize; + device->SetWindowFullscreen = WINRT_SetWindowFullscreen; + device->DestroyWindow = WINRT_DestroyWindow; + device->SetDisplayMode = WINRT_SetDisplayMode; + device->PumpEvents = WINRT_PumpEvents; + device->GetWindowWMInfo = WINRT_GetWindowWMInfo; + device->SuspendScreenSaver = WINRT_SuspendScreenSaver; + +#if NTDDI_VERSION >= NTDDI_WIN10 + device->HasScreenKeyboardSupport = WINRT_HasScreenKeyboardSupport; + device->ShowScreenKeyboard = WINRT_ShowScreenKeyboard; + device->HideScreenKeyboard = WINRT_HideScreenKeyboard; + device->IsScreenKeyboardShown = WINRT_IsScreenKeyboardShown; + + WINTRT_InitialiseInputPaneEvents(device); +#endif + +#ifdef SDL_VIDEO_OPENGL_EGL + device->GL_LoadLibrary = WINRT_GLES_LoadLibrary; + device->GL_GetProcAddress = WINRT_GLES_GetProcAddress; + device->GL_UnloadLibrary = WINRT_GLES_UnloadLibrary; + device->GL_CreateContext = WINRT_GLES_CreateContext; + device->GL_MakeCurrent = WINRT_GLES_MakeCurrent; + device->GL_SetSwapInterval = WINRT_GLES_SetSwapInterval; + device->GL_GetSwapInterval = WINRT_GLES_GetSwapInterval; + device->GL_SwapWindow = WINRT_GLES_SwapWindow; + device->GL_DeleteContext = WINRT_GLES_DeleteContext; +#endif + device->free = WINRT_DeleteDevice; + + return device; +} + +#define WINRTVID_DRIVER_NAME "winrt" +VideoBootStrap WINRT_bootstrap = { + WINRTVID_DRIVER_NAME, "SDL WinRT video driver", + WINRT_CreateDevice +}; + +static void SDLCALL +WINRT_SetDisplayOrientationsPreference(void *userdata, const char *name, const char *oldValue, const char *newValue) +{ + SDL_assert(SDL_strcmp(name, SDL_HINT_ORIENTATIONS) == 0); + + /* HACK: prevent SDL from altering an app's .appxmanifest-set orientation + * from being changed on startup, by detecting when SDL_HINT_ORIENTATIONS + * is getting registered. + * + * TODO, WinRT: consider reading in an app's .appxmanifest file, and apply its orientation when 'newValue == NULL'. + */ + if ((oldValue == NULL) && (newValue == NULL)) { + return; + } + + // Start with no orientation flags, then add each in as they're parsed + // from newValue. + unsigned int orientationFlags = 0; + if (newValue) { + 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 WinRT [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). + WINRT_DISPLAY_PROPERTY(AutoRotationPreferences) = (DisplayOrientations) orientationFlags; +} + +int +WINRT_VideoInit(_THIS) +{ + SDL_VideoData * driverdata = (SDL_VideoData *) _this->driverdata; + if (WINRT_InitModes(_this) < 0) { + return -1; + } + + // Register the hint, SDL_HINT_ORIENTATIONS, with SDL. + // TODO, WinRT: 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_AddHintCallback(SDL_HINT_ORIENTATIONS, WINRT_SetDisplayOrientationsPreference, NULL); + + WINRT_InitMouse(_this); + WINRT_InitTouch(_this); + WINRT_InitGameBar(_this); + if (driverdata) { + /* Initialize screensaver-disabling support */ + driverdata->displayRequest = WINRT_CreateDisplayRequest(_this); + } + return 0; +} + +extern "C" +Uint32 D3D11_DXGIFormatToSDLPixelFormat(DXGI_FORMAT dxgiFormat); + +static void +WINRT_DXGIModeToSDLDisplayMode(const DXGI_MODE_DESC * dxgiMode, SDL_DisplayMode * sdlMode) +{ + SDL_zerop(sdlMode); + sdlMode->w = dxgiMode->Width; + sdlMode->h = dxgiMode->Height; + sdlMode->refresh_rate = dxgiMode->RefreshRate.Numerator / dxgiMode->RefreshRate.Denominator; + sdlMode->format = D3D11_DXGIFormatToSDLPixelFormat(dxgiMode->Format); +} + +static int +WINRT_AddDisplaysForOutput (_THIS, IDXGIAdapter1 * dxgiAdapter1, int outputIndex) +{ + HRESULT hr; + IDXGIOutput * dxgiOutput = NULL; + DXGI_OUTPUT_DESC dxgiOutputDesc; + SDL_VideoDisplay display; + char * displayName = NULL; + UINT numModes; + DXGI_MODE_DESC * dxgiModes = NULL; + int functionResult = -1; /* -1 for failure, 0 for success */ + DXGI_MODE_DESC modeToMatch, closestMatch; + + SDL_zero(display); + + hr = dxgiAdapter1->EnumOutputs(outputIndex, &dxgiOutput); + if (FAILED(hr)) { + if (hr != DXGI_ERROR_NOT_FOUND) { + WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIAdapter1::EnumOutputs failed", hr); + } + goto done; + } + + hr = dxgiOutput->GetDesc(&dxgiOutputDesc); + if (FAILED(hr)) { + WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIOutput::GetDesc failed", hr); + goto done; + } + + SDL_zero(modeToMatch); + modeToMatch.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + modeToMatch.Width = (dxgiOutputDesc.DesktopCoordinates.right - dxgiOutputDesc.DesktopCoordinates.left); + modeToMatch.Height = (dxgiOutputDesc.DesktopCoordinates.bottom - dxgiOutputDesc.DesktopCoordinates.top); + hr = dxgiOutput->FindClosestMatchingMode(&modeToMatch, &closestMatch, NULL); + if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) { + /* DXGI_ERROR_NOT_CURRENTLY_AVAILABLE gets returned by IDXGIOutput::FindClosestMatchingMode + when running under the Windows Simulator, which uses Remote Desktop (formerly known as Terminal + Services) under the hood. According to the MSDN docs for the similar function, + IDXGIOutput::GetDisplayModeList, DXGI_ERROR_NOT_CURRENTLY_AVAILABLE is returned if and + when an app is run under a Terminal Services session, hence the assumption. + + In this case, just add an SDL display mode, with approximated values. + */ + SDL_DisplayMode mode; + SDL_zero(mode); + display.name = "Windows Simulator / Terminal Services Display"; + mode.w = (dxgiOutputDesc.DesktopCoordinates.right - dxgiOutputDesc.DesktopCoordinates.left); + mode.h = (dxgiOutputDesc.DesktopCoordinates.bottom - dxgiOutputDesc.DesktopCoordinates.top); + mode.format = DXGI_FORMAT_B8G8R8A8_UNORM; + mode.refresh_rate = 0; /* Display mode is unknown, so just fill in zero, as specified by SDL's header files */ + display.desktop_mode = mode; + display.current_mode = mode; + if ( ! SDL_AddDisplayMode(&display, &mode)) { + goto done; + } + } else if (FAILED(hr)) { + WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIOutput::FindClosestMatchingMode failed", hr); + goto done; + } else { + displayName = WIN_StringToUTF8(dxgiOutputDesc.DeviceName); + display.name = displayName; + WINRT_DXGIModeToSDLDisplayMode(&closestMatch, &display.desktop_mode); + display.current_mode = display.desktop_mode; + + hr = dxgiOutput->GetDisplayModeList(DXGI_FORMAT_B8G8R8A8_UNORM, 0, &numModes, NULL); + if (FAILED(hr)) { + if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) { + // TODO, WinRT: make sure display mode(s) are added when using Terminal Services / Windows Simulator + } + WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIOutput::GetDisplayModeList [get mode list size] failed", hr); + goto done; + } + + dxgiModes = (DXGI_MODE_DESC *)SDL_calloc(numModes, sizeof(DXGI_MODE_DESC)); + if ( ! dxgiModes) { + SDL_OutOfMemory(); + goto done; + } + + hr = dxgiOutput->GetDisplayModeList(DXGI_FORMAT_B8G8R8A8_UNORM, 0, &numModes, dxgiModes); + if (FAILED(hr)) { + WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIOutput::GetDisplayModeList [get mode contents] failed", hr); + goto done; + } + + for (UINT i = 0; i < numModes; ++i) { + SDL_DisplayMode sdlMode; + WINRT_DXGIModeToSDLDisplayMode(&dxgiModes[i], &sdlMode); + SDL_AddDisplayMode(&display, &sdlMode); + } + } + + if (SDL_AddVideoDisplay(&display, SDL_FALSE) < 0) { + goto done; + } + + functionResult = 0; /* 0 for Success! */ +done: + if (dxgiModes) { + SDL_free(dxgiModes); + } + if (dxgiOutput) { + dxgiOutput->Release(); + } + if (displayName) { + SDL_free(displayName); + } + return functionResult; +} + +static int +WINRT_AddDisplaysForAdapter (_THIS, IDXGIFactory2 * dxgiFactory2, int adapterIndex) +{ + HRESULT hr; + IDXGIAdapter1 * dxgiAdapter1; + + hr = dxgiFactory2->EnumAdapters1(adapterIndex, &dxgiAdapter1); + if (FAILED(hr)) { + if (hr != DXGI_ERROR_NOT_FOUND) { + WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIFactory1::EnumAdapters1() failed", hr); + } + return -1; + } + + for (int outputIndex = 0; ; ++outputIndex) { + if (WINRT_AddDisplaysForOutput(_this, dxgiAdapter1, outputIndex) < 0) { + /* HACK: The Windows App Certification Kit 10.0 can fail, when + running the Store Apps' test, "Direct3D Feature Test". The + certification kit's error is: + + "Application App was not running at the end of the test. It likely crashed or was terminated for having become unresponsive." + + This was caused by SDL/WinRT's DXGI failing to report any + outputs. Attempts to get the 1st display-output from the + 1st display-adapter can fail, with IDXGIAdapter::EnumOutputs + returning DXGI_ERROR_NOT_FOUND. This could be a bug in Windows, + the Windows App Certification Kit, or possibly in SDL/WinRT's + display detection code. Either way, try to detect when this + happens, and use a hackish means to create a reasonable-as-possible + 'display mode'. -- DavidL + */ + if (adapterIndex == 0 && outputIndex == 0) { + SDL_VideoDisplay display; + SDL_DisplayMode mode; +#if SDL_WINRT_USE_APPLICATIONVIEW + ApplicationView ^ appView = ApplicationView::GetForCurrentView(); +#endif + CoreWindow ^ coreWin = CoreWindow::GetForCurrentThread(); + SDL_zero(display); + SDL_zero(mode); + display.name = "DXGI Display-detection Workaround"; + + /* HACK: ApplicationView's VisibleBounds property, appeared, via testing, to + give a better approximation of display-size, than did CoreWindow's + Bounds property, insofar that ApplicationView::VisibleBounds seems like + it will, at least some of the time, give the full display size (during the + failing test), whereas CoreWindow might not. -- DavidL + */ + +#if (NTDDI_VERSION >= NTDDI_WIN10) || (SDL_WINRT_USE_APPLICATIONVIEW && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + mode.w = WINRT_DIPS_TO_PHYSICAL_PIXELS(appView->VisibleBounds.Width); + mode.h = WINRT_DIPS_TO_PHYSICAL_PIXELS(appView->VisibleBounds.Height); +#else + /* On platform(s) that do not support VisibleBounds, such as Windows 8.1, + fall back to CoreWindow's Bounds property. + */ + mode.w = WINRT_DIPS_TO_PHYSICAL_PIXELS(coreWin->Bounds.Width); + mode.h = WINRT_DIPS_TO_PHYSICAL_PIXELS(coreWin->Bounds.Height); +#endif + + mode.format = DXGI_FORMAT_B8G8R8A8_UNORM; + mode.refresh_rate = 0; /* Display mode is unknown, so just fill in zero, as specified by SDL's header files */ + display.desktop_mode = mode; + display.current_mode = mode; + if ((SDL_AddDisplayMode(&display, &mode) < 0) || + (SDL_AddVideoDisplay(&display, SDL_FALSE) < 0)) + { + return SDL_SetError("Failed to apply DXGI Display-detection workaround"); + } + } + + break; + } + } + + dxgiAdapter1->Release(); + return 0; +} + +int +WINRT_InitModes(_THIS) +{ + /* HACK: Initialize a single display, for whatever screen the app's + CoreApplicationView is on. + TODO, WinRT: Try initializing multiple displays, one for each monitor. + Appropriate WinRT APIs for this seem elusive, though. -- DavidL + */ + + HRESULT hr; + IDXGIFactory2 * dxgiFactory2 = NULL; + + hr = CreateDXGIFactory1(SDL_IID_IDXGIFactory2, (void **)&dxgiFactory2); + if (FAILED(hr)) { + return WIN_SetErrorFromHRESULT(__FUNCTION__ ", CreateDXGIFactory1() failed", hr); + } + + for (int adapterIndex = 0; ; ++adapterIndex) { + if (WINRT_AddDisplaysForAdapter(_this, dxgiFactory2, adapterIndex) < 0) { + break; + } + } + + return 0; +} + +static int +WINRT_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) +{ + return 0; +} + +void +WINRT_VideoQuit(_THIS) +{ + SDL_VideoData * driverdata = (SDL_VideoData *) _this->driverdata; + if (driverdata && driverdata->displayRequest) { + driverdata->displayRequest->Release(); + driverdata->displayRequest = NULL; + } + WINRT_QuitGameBar(_this); + WINRT_QuitMouse(_this); +} + +static const Uint32 WINRT_DetectableFlags = + SDL_WINDOW_MAXIMIZED | + SDL_WINDOW_FULLSCREEN_DESKTOP | + SDL_WINDOW_SHOWN | + SDL_WINDOW_HIDDEN | + SDL_WINDOW_MOUSE_FOCUS; + +extern "C" Uint32 +WINRT_DetectWindowFlags(SDL_Window * window) +{ + Uint32 latestFlags = 0; + SDL_WindowData * data = (SDL_WindowData *) window->driverdata; + bool is_fullscreen = false; + +#if SDL_WINRT_USE_APPLICATIONVIEW + if (data->appView) { + is_fullscreen = data->appView->IsFullScreenMode; + } +#elif (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) || (NTDDI_VERSION == NTDDI_WIN8) + is_fullscreen = true; +#endif + + if (data->coreWindow.Get()) { + if (is_fullscreen) { + SDL_VideoDisplay * display = SDL_GetDisplayForWindow(window); + int w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width); + int h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height); + +#if (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP) || (NTDDI_VERSION > NTDDI_WIN8) + // On all WinRT platforms, except for WinPhone 8.0, rotate the + // window size. This is needed to properly calculate + // fullscreen vs. maximized. + const DisplayOrientations currentOrientation = WINRT_DISPLAY_PROPERTY(CurrentOrientation); + switch (currentOrientation) { +#if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + case DisplayOrientations::Landscape: + case DisplayOrientations::LandscapeFlipped: +#else + case DisplayOrientations::Portrait: + case DisplayOrientations::PortraitFlipped: +#endif + { + int tmp = w; + w = h; + h = tmp; + } break; + } +#endif + + if (display->desktop_mode.w != w || display->desktop_mode.h != h) { + latestFlags |= SDL_WINDOW_MAXIMIZED; + } else { + latestFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + } + } + + if (data->coreWindow->Visible) { + latestFlags |= SDL_WINDOW_SHOWN; + } else { + latestFlags |= SDL_WINDOW_HIDDEN; + } + +#if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) && (NTDDI_VERSION < NTDDI_WINBLUE) + // data->coreWindow->PointerPosition is not supported on WinPhone 8.0 + latestFlags |= SDL_WINDOW_MOUSE_FOCUS; +#else + if (data->coreWindow->Visible && data->coreWindow->Bounds.Contains(data->coreWindow->PointerPosition)) { + latestFlags |= SDL_WINDOW_MOUSE_FOCUS; + } +#endif + } + + return latestFlags; +} + +// TODO, WinRT: consider removing WINRT_UpdateWindowFlags, and just calling WINRT_DetectWindowFlags as-appropriate (with appropriate calls to SDL_SendWindowEvent) +void +WINRT_UpdateWindowFlags(SDL_Window * window, Uint32 mask) +{ + mask &= WINRT_DetectableFlags; + if (window) { + Uint32 apply = WINRT_DetectWindowFlags(window); + if ((apply & mask) & SDL_WINDOW_FULLSCREEN) { + window->last_fullscreen_flags = window->flags; // seems necessary to programmatically un-fullscreen, via SDL APIs + } + window->flags = (window->flags & ~mask) | (apply & mask); + } +} + +static bool +WINRT_IsCoreWindowActive(CoreWindow ^ coreWindow) +{ + /* WinRT does not appear to offer API(s) to determine window-activation state, + at least not that I am aware of in Win8 - Win10. As such, SDL tracks this + itself, via window-activation events. + + If there *is* an API to track this, it should probably get used instead + of the following hack (that uses "SDLHelperWindowActivationState"). + -- DavidL. + */ + if (coreWindow->CustomProperties->HasKey("SDLHelperWindowActivationState")) { + CoreWindowActivationState activationState = \ + safe_cast(coreWindow->CustomProperties->Lookup("SDLHelperWindowActivationState")); + return (activationState != CoreWindowActivationState::Deactivated); + } + + /* Assume that non-SDL tracked windows are active, although this should + probably be avoided, if possible. + + This might not even be possible, in normal SDL use, at least as of + this writing (Dec 22, 2015; via latest hg.libsdl.org/SDL clone) -- DavidL + */ + return true; +} + +int +WINRT_CreateWindow(_THIS, SDL_Window * window) +{ + // Make sure that only one window gets created, at least until multimonitor + // support is added. + if (WINRT_GlobalSDLWindow != NULL) { + return SDL_SetError("WinRT only supports one window"); + } + + SDL_WindowData *data = new SDL_WindowData; /* use 'new' here as SDL_WindowData may use WinRT/C++ types */ + if (!data) { + return SDL_OutOfMemory(); + } + window->driverdata = data; + data->sdlWindow = window; + + /* To note, when XAML support is enabled, access to the CoreWindow will not + be possible, at least not via the SDL/XAML thread. Attempts to access it + from there will throw exceptions. As such, the SDL_WindowData's + 'coreWindow' field will only be set (to a non-null value) if XAML isn't + enabled. + */ + if (!WINRT_XAMLWasEnabled) { + data->coreWindow = CoreWindow::GetForCurrentThread(); +#if SDL_WINRT_USE_APPLICATIONVIEW + data->appView = ApplicationView::GetForCurrentView(); +#endif + } + + /* Make note of the requested window flags, before they start getting changed. */ + const Uint32 requestedFlags = window->flags; + +#if SDL_VIDEO_OPENGL_EGL + /* Setup the EGL surface, but only if OpenGL ES 2 was requested. */ + if (!(window->flags & SDL_WINDOW_OPENGL)) { + /* OpenGL ES 2 wasn't requested. Don't set up an EGL surface. */ + data->egl_surface = EGL_NO_SURFACE; + } else { + /* OpenGL ES 2 was reuqested. Set up an EGL surface. */ + SDL_VideoData * video_data = (SDL_VideoData *)_this->driverdata; + + /* Call SDL_EGL_ChooseConfig and eglCreateWindowSurface directly, + * rather than via SDL_EGL_CreateSurface, as older versions of + * ANGLE/WinRT may require that a C++ object, ComPtr, + * be passed into eglCreateWindowSurface. + */ + if (SDL_EGL_ChooseConfig(_this) != 0) { + /* SDL_EGL_ChooseConfig failed, SDL_GetError() should have info */ + return -1; + } + + if (video_data->winrtEglWindow) { /* ... is the 'old' version of ANGLE/WinRT being used? */ + /* Attempt to create a window surface using older versions of + * ANGLE/WinRT: + */ + Microsoft::WRL::ComPtr cpp_winrtEglWindow = video_data->winrtEglWindow; + data->egl_surface = ((eglCreateWindowSurface_Old_Function)_this->egl_data->eglCreateWindowSurface)( + _this->egl_data->egl_display, + _this->egl_data->egl_config, + cpp_winrtEglWindow, NULL); + if (data->egl_surface == NULL) { + return SDL_EGL_SetError("unable to create EGL native-window surface", "eglCreateWindowSurface"); + } + } else if (data->coreWindow.Get() != nullptr) { + /* Attempt to create a window surface using newer versions of + * ANGLE/WinRT: + */ + IInspectable * coreWindowAsIInspectable = reinterpret_cast(data->coreWindow.Get()); + data->egl_surface = _this->egl_data->eglCreateWindowSurface( + _this->egl_data->egl_display, + _this->egl_data->egl_config, + (NativeWindowType)coreWindowAsIInspectable, + NULL); + if (data->egl_surface == NULL) { + return SDL_EGL_SetError("unable to create EGL native-window surface", "eglCreateWindowSurface"); + } + } else { + return SDL_SetError("No supported means to create an EGL window surface are available"); + } + } +#endif + + /* Determine as many flags dynamically, as possible. */ + window->flags = + SDL_WINDOW_BORDERLESS | + SDL_WINDOW_RESIZABLE; + +#if SDL_VIDEO_OPENGL_EGL + if (data->egl_surface) { + window->flags |= SDL_WINDOW_OPENGL; + } +#endif + + if (WINRT_XAMLWasEnabled) { + /* TODO, WinRT: set SDL_Window size, maybe position too, from XAML control */ + window->x = 0; + window->y = 0; + window->flags |= SDL_WINDOW_SHOWN; + SDL_SetMouseFocus(NULL); // TODO: detect this + SDL_SetKeyboardFocus(NULL); // TODO: detect this + } else { + /* WinRT 8.x apps seem to live in an environment where the OS controls the + app's window size, with some apps being fullscreen, depending on + user choice of various things. For now, just adapt the SDL_Window to + whatever Windows set-up as the native-window's geometry. + */ + window->x = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Left); + window->y = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Top); +#if NTDDI_VERSION < NTDDI_WIN10 + /* On WinRT 8.x / pre-Win10, just use the size we were given. */ + window->w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width); + window->h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height); +#else + /* On Windows 10, we occasionally get control over window size. For windowed + mode apps, try this. + */ + bool didSetSize = false; + if (!(requestedFlags & SDL_WINDOW_FULLSCREEN)) { + const Windows::Foundation::Size size(WINRT_PHYSICAL_PIXELS_TO_DIPS(window->w), + WINRT_PHYSICAL_PIXELS_TO_DIPS(window->h)); + didSetSize = data->appView->TryResizeView(size); + } + if (!didSetSize) { + /* We either weren't able to set the window size, or a request for + fullscreen was made. Get window-size info from the OS. + */ + window->w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width); + window->h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height); + } +#endif + + WINRT_UpdateWindowFlags( + window, + 0xffffffff /* Update any window flag(s) that WINRT_UpdateWindow can handle */ + ); + + /* Try detecting if the window is active */ + bool isWindowActive = WINRT_IsCoreWindowActive(data->coreWindow.Get()); + if (isWindowActive) { + SDL_SetKeyboardFocus(window); + } + } + + /* Make sure the WinRT app's IFramworkView can post events on + behalf of SDL: + */ + WINRT_GlobalSDLWindow = window; + + /* All done! */ + return 0; +} + +void +WINRT_SetWindowSize(_THIS, SDL_Window * window) +{ +#if NTDDI_VERSION >= NTDDI_WIN10 + SDL_WindowData * data = (SDL_WindowData *)window->driverdata; + const Windows::Foundation::Size size(WINRT_PHYSICAL_PIXELS_TO_DIPS(window->w), + WINRT_PHYSICAL_PIXELS_TO_DIPS(window->h)); + data->appView->TryResizeView(size); // TODO, WinRT: return failure (to caller?) from TryResizeView() +#endif +} + +void +WINRT_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen) +{ +#if NTDDI_VERSION >= NTDDI_WIN10 + SDL_WindowData * data = (SDL_WindowData *)window->driverdata; + bool isWindowActive = WINRT_IsCoreWindowActive(data->coreWindow.Get()); + if (isWindowActive) { + if (fullscreen) { + if (!data->appView->IsFullScreenMode) { + data->appView->TryEnterFullScreenMode(); // TODO, WinRT: return failure (to caller?) from TryEnterFullScreenMode() + } + } else { + if (data->appView->IsFullScreenMode) { + data->appView->ExitFullScreenMode(); + } + } + } +#endif +} + + +void +WINRT_DestroyWindow(_THIS, SDL_Window * window) +{ + SDL_WindowData * data = (SDL_WindowData *) window->driverdata; + + if (WINRT_GlobalSDLWindow == window) { + WINRT_GlobalSDLWindow = NULL; + } + + if (data) { + // Delete the internal window data: + delete data; + data = NULL; + window->driverdata = 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_WINRT; + info->info.winrt.window = reinterpret_cast(data->coreWindow.Get()); + return SDL_TRUE; + } else { + SDL_SetError("Application not compiled with SDL %d", + SDL_MAJOR_VERSION); + return SDL_FALSE; + } + return SDL_FALSE; +} + +static ABI::Windows::System::Display::IDisplayRequest * +WINRT_CreateDisplayRequest(_THIS) +{ + /* Setup a WinRT DisplayRequest object, usable for enabling/disabling screensaver requests */ + wchar_t *wClassName = L"Windows.System.Display.DisplayRequest"; + HSTRING hClassName; + IActivationFactory *pActivationFactory = NULL; + IInspectable * pDisplayRequestRaw = nullptr; + ABI::Windows::System::Display::IDisplayRequest * pDisplayRequest = nullptr; + HRESULT hr; + + hr = ::WindowsCreateString(wClassName, (UINT32)SDL_wcslen(wClassName), &hClassName); + if (FAILED(hr)) { + goto done; + } + + hr = Windows::Foundation::GetActivationFactory(hClassName, &pActivationFactory); + if (FAILED(hr)) { + goto done; + } + + hr = pActivationFactory->ActivateInstance(&pDisplayRequestRaw); + if (FAILED(hr)) { + goto done; + } + + hr = pDisplayRequestRaw->QueryInterface(SDL_IID_IDisplayRequest, (void **) &pDisplayRequest); + if (FAILED(hr)) { + goto done; + } + +done: + if (pDisplayRequestRaw) { + pDisplayRequestRaw->Release(); + } + if (pActivationFactory) { + pActivationFactory->Release(); + } + if (hClassName) { + ::WindowsDeleteString(hClassName); + } + + return pDisplayRequest; +} + +void +WINRT_SuspendScreenSaver(_THIS) +{ + SDL_VideoData *driverdata = (SDL_VideoData *)_this->driverdata; + if (driverdata && driverdata->displayRequest) { + ABI::Windows::System::Display::IDisplayRequest * displayRequest = (ABI::Windows::System::Display::IDisplayRequest *) driverdata->displayRequest; + if (_this->suspend_screensaver) { + displayRequest->RequestActive(); + } else { + displayRequest->RequestRelease(); + } + } +} + +#endif /* SDL_VIDEO_DRIVER_WINRT */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/winrt/SDL_winrtvideo_cpp.h b/src/video/winrt/SDL_winrtvideo_cpp.h new file mode 100644 index 000000000..8e6d63bdc --- /dev/null +++ b/src/video/winrt/SDL_winrtvideo_cpp.h @@ -0,0 +1,106 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 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. +*/ + +/* Windows includes: */ +#include +#ifdef __cplusplus_winrt +#include +#endif + +/* SDL includes: */ +#include "SDL_video.h" +#include "SDL_events.h" + +#if NTDDI_VERSION >= NTDDI_WINBLUE /* ApplicationView's functionality only becomes + useful for SDL in Win[Phone] 8.1 and up. + Plus, it is not available at all in WinPhone 8.0. */ +#define SDL_WINRT_USE_APPLICATIONVIEW 1 +#endif + +extern "C" { +#include "../SDL_sysvideo.h" +#include "../SDL_egl_c.h" +} + +/* Private display data */ +typedef struct SDL_VideoData { + /* An object created by ANGLE/WinRT (OpenGL ES 2 for WinRT) that gets + * passed to eglGetDisplay and eglCreateWindowSurface: + */ + IUnknown *winrtEglWindow; + + /* Event token(s), for unregistering WinRT event handler(s). + These are just a struct with a 64-bit integer inside them + */ + Windows::Foundation::EventRegistrationToken gameBarIsInputRedirectedToken; + + /* A WinRT DisplayRequest, used for implementing SDL_*ScreenSaver() functions. + * This is really a pointer to a 'ABI::Windows::System::Display::IDisplayRequest *', + * It's casted to 'IUnknown *', to help with building SDL. + */ + IUnknown *displayRequest; +} SDL_VideoData; + +/* The global, WinRT, SDL Window. + For now, SDL/WinRT only supports one window (due to platform limitations of + WinRT. +*/ +extern SDL_Window * WINRT_GlobalSDLWindow; + +/* Updates one or more SDL_Window flags, by querying the OS' native windowing APIs. + SDL_Window flags that can be updated should be specified in 'mask'. +*/ +extern void WINRT_UpdateWindowFlags(SDL_Window * window, Uint32 mask); +extern "C" Uint32 WINRT_DetectWindowFlags(SDL_Window * window); /* detects flags w/o applying them */ + +/* Display mode internals */ +//typedef struct +//{ +// Windows::Graphics::Display::DisplayOrientations currentOrientation; +//} SDL_DisplayModeData; + +#ifdef __cplusplus_winrt + +/* A convenience macro to get a WinRT display property */ +#if NTDDI_VERSION > NTDDI_WIN8 +#define WINRT_DISPLAY_PROPERTY(NAME) (Windows::Graphics::Display::DisplayInformation::GetForCurrentView()->NAME) +#else +#define WINRT_DISPLAY_PROPERTY(NAME) (Windows::Graphics::Display::DisplayProperties::NAME) +#endif + +/* Converts DIPS to/from physical pixels */ +#define WINRT_DIPS_TO_PHYSICAL_PIXELS(DIPS) ((int)(0.5f + (((float)(DIPS) * (float)WINRT_DISPLAY_PROPERTY(LogicalDpi)) / 96.f))) +#define WINRT_PHYSICAL_PIXELS_TO_DIPS(PHYSPIX) (((float)(PHYSPIX) * 96.f)/WINRT_DISPLAY_PROPERTY(LogicalDpi)) + +/* Internal window data */ +struct SDL_WindowData +{ + SDL_Window *sdlWindow; + Platform::Agile coreWindow; +#ifdef SDL_VIDEO_OPENGL_EGL + EGLSurface egl_surface; +#endif +#if SDL_WINRT_USE_APPLICATIONVIEW + Windows::UI::ViewManagement::ApplicationView ^ appView; +#endif +}; + +#endif // ifdef __cplusplus_winrt